Browse Source

Refactor triggers to accept multiple actions. This will pave the way for groups to be controlled.

Robert Marshall 6 years ago
parent
commit
6c2ea6e0c2

+ 6 - 6
HAServer.Tests.Integration/IntegrationTests.cs

@@ -28,9 +28,9 @@ namespace HAServer.Tests.Integration {
 			var @switch = new Switch(bus, "switch", "Switch", "1", "0") { On = true };
 			devices.AddDevice(@switch);
 
-			var trigger = new Trigger(bus, "trigger", TriggerType.Any, @switch.TurnOff, logProvider.Object);
-			var condition = new Condition(Comparator.Equal, "solar_event", "civil_twilight_end");
-			trigger.AddCondition(condition);
+			var conditions = new[] {new Condition(Comparator.Equal, "solar_event", "civil_twilight_end")};
+			var actions = new[] {new TriggerAction(TriggerActionType.Device, @switch.Topic, @switch.TurnOff)};
+			var trigger = new Trigger(bus, "trigger", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			bus.PublishMessage("time", "21:11:25", null);
 			Assert.That(@switch.On, Is.False);
@@ -57,9 +57,9 @@ namespace HAServer.Tests.Integration {
 			devices.AddDevice(sensor);
 			devices.AddDevice(input);
 
-			var trigger2 = new Trigger(bus, "trigger2", TriggerType.Any, @switch.TurnOn, logProvider.Object);
-			var condition2 = new DeviceCondition(Comparator.LessThan, "sensor", input);
-			trigger2.AddCondition(condition2);
+			var conditions2 = new[] { new DeviceCondition(Comparator.LessThan, "sensor", input)};
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, @switch.Topic, @switch.TurnOn) };
+			var trigger2 = new Trigger(bus, "trigger2", TriggerType.Any, actions, conditions2, logProvider.Object);
 
 			bus.PublishMessage("input", "2", null);
 			bus.PublishMessage("sensor", "1", null);

+ 30 - 8
HAServer.Tests/Core/TriggerConfigTests.cs

@@ -30,8 +30,18 @@ namespace HAServer.Tests.Core {
           ""Topic"": ""event""
         }
       ],
-      ""DeviceTopic"": ""topic"",
-      ""DeviceMethod"": ""Method""
+      ""Actions"": [
+        {
+          ""Type"": 0,
+          ""Identifier"": ""topic"",
+          ""Method"": ""Method""
+        },
+        {
+          ""Type"": 1,
+          ""Identifier"": ""group"",
+          ""Method"": ""TurnOn""
+        }
+      ]
     }
   ]
 }";
@@ -41,7 +51,7 @@ namespace HAServer.Tests.Core {
 			var config = new TriggerConfig();
 			var bus = new Mock<IMessageBus>();
 			var logProvider = new Mock<ILogProvider>();
-			var trigger = new Trigger(bus.Object, string.Empty, TriggerType.All, () => { }, logProvider.Object);
+			var trigger = new Trigger(bus.Object, string.Empty, TriggerType.All, Enumerable.Empty<TriggerAction>(), Enumerable.Empty<Condition>(), logProvider.Object);
 			config.Add(trigger);
 			Assert.That(config.Triggers, Contains.Item(trigger));
 		}
@@ -51,7 +61,7 @@ namespace HAServer.Tests.Core {
 			var config = new TriggerConfig();
 			var bus = new Mock<IMessageBus>();
 			var logProvider = new Mock<ILogProvider>();
-			var trigger = new Trigger(bus.Object, string.Empty, TriggerType.All, () => { }, logProvider.Object);
+			var trigger = new Trigger(bus.Object, string.Empty, TriggerType.All, Enumerable.Empty<TriggerAction>(), Enumerable.Empty<Condition>(), logProvider.Object);
 			config.Add(trigger);
 			config.Remove(trigger.Id);
 			Assert.That(config.Triggers, Is.Empty);
@@ -65,11 +75,18 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var device = new DeviceStub(bus.Object, "topic", "device");
-			var trigger = new Trigger(bus.Object, "name", TriggerType.Any, device.Method, logProvider.Object) {
+			var group=new DeviceGroup("group");
+			var actions = new[] {
+				new TriggerAction(TriggerActionType.Device, device.Topic, device.Method),
+				new TriggerAction(TriggerActionType.Group, group.Name, group.TurnOn)
+			};
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "event", "value"),
+				new DeviceCondition(Comparator.Equal, "event", device)
+			};
+			var trigger = new Trigger(bus.Object, "name", TriggerType.Any, actions, conditions, logProvider.Object) {
 				Enabled = false
 			};
-			trigger.AddCondition(new Condition(Comparator.Equal, "event", "value"));
-			trigger.AddCondition(new DeviceCondition(Comparator.Equal, "event", device));
 			config.Add(trigger);
 
 			var testJsonPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "test.json");
@@ -86,13 +103,18 @@ namespace HAServer.Tests.Core {
 			var bus = new Mock<IMessageBus>();
 			var logProvider = new Mock<ILogProvider>();
 			var baseDevices = new Dictionary<string, BaseDevice> {{"topic", new DeviceStub(bus.Object, "topic", "device")}};
-			var deviceConfig = new DeviceConfig(baseDevices, new Dictionary<string, string[]>());
+			var groups = new Dictionary<string, string[]> {{"group", new[] {"topic"}}};
+			var deviceConfig = new DeviceConfig(baseDevices, groups);
 			var config = TriggerConfig.Load(testJsonPath, bus.Object, deviceConfig, logProvider.Object);
 
 			var trigger = config.Triggers.FirstOrDefault(t => t.Name=="name");
 			Assert.That(trigger, Is.Not.Null);
 			Assert.That(trigger.Conditions.First(), Is.TypeOf<Condition>());
 			Assert.That(trigger.Conditions.Last(), Is.TypeOf<DeviceCondition>());
+			Assert.That(trigger.Actions.First().Identifier, Is.EqualTo("topic"));
+			Assert.That(trigger.Actions.First().Type, Is.EqualTo(TriggerActionType.Device));
+			Assert.That(trigger.Actions.Last().Identifier, Is.EqualTo("group"));
+			Assert.That(trigger.Actions.Last().Type, Is.EqualTo(TriggerActionType.Group));
 			Assert.That(trigger.Enabled, Is.False);
 			File.Delete(testJsonPath);
 		}

+ 91 - 53
HAServer.Tests/Core/TriggerTests.cs

@@ -12,10 +12,10 @@ namespace HAServer.Tests.Core {
 		public void Constructor_IdCounterIncrements() {
 			var bus = new Mock<IMessageBus>();
 			var logProvider = new Mock<ILogProvider>();
-			Action action = () => { };
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => { }) };
 			var startId = Trigger.CurrentId;
-			var trigger1 = new Trigger(bus.Object, "name", TriggerType.All, action, Enumerable.Empty<Condition>(), logProvider.Object);
-			var trigger2 = new Trigger(bus.Object, "name", TriggerType.All, action, Enumerable.Empty<Condition>(), logProvider.Object);
+			var trigger1 = new Trigger(bus.Object, "name", TriggerType.All, actions, Enumerable.Empty<Condition>(), logProvider.Object);
+			var trigger2 = new Trigger(bus.Object, "name", TriggerType.All, actions, Enumerable.Empty<Condition>(), logProvider.Object);
 			Assert.That(trigger1.Id, Is.EqualTo(startId));
 			Assert.That(trigger2.Id, Is.EqualTo(startId + 1));
 		}
@@ -26,13 +26,14 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 			var condition = new Condition(Comparator.Equal, "test", "");
 			var conditions = new[] { condition };
-			Action action = () => { };
-			var trigger = new Trigger(bus.Object, "name", TriggerType.All, action, conditions, logProvider.Object);
+			var action = new TriggerAction(TriggerActionType.Device, "device", () => { });
+			var actions = new[] { action };
+			var trigger = new Trigger(bus.Object, "name", TriggerType.All, actions, conditions, logProvider.Object);
 
 			Assert.That(trigger.Name, Is.EqualTo("name"));
 			Assert.That(trigger.TriggerType, Is.EqualTo(TriggerType.All));
-			Assert.That(trigger.DeviceMethod, Is.EqualTo(action.Method.Name));
 			Assert.That(trigger.Conditions, Contains.Item(condition));
+			Assert.That(trigger.Actions, Contains.Item(action));
 			Assert.That(trigger.Enabled, Is.True);
 		}
 
@@ -41,10 +42,12 @@ namespace HAServer.Tests.Core {
 			var bus = new Mock<IMessageBus>();
 			var logProvider = new Mock<ILogProvider>();
 
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => { }, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
-			trigger.AddCondition(new Condition(Comparator.NotEqual, "test", "off"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => { }) };
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "test", "on"),
+				new Condition(Comparator.NotEqual, "test", "off")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			Assert.That(trigger.Conditions.Count, Is.EqualTo(2));
 		}
@@ -55,9 +58,9 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] { new Condition(Comparator.Equal, "test", "on") };
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "on");
 
@@ -70,9 +73,9 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => triggered=true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] { new Condition(Comparator.Equal, "test", "on") };
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "off");
 
@@ -85,10 +88,12 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
-			trigger.AddCondition(new Condition(Comparator.Equal, "test2", "on"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "test", "on"),
+				new Condition(Comparator.Equal, "test2", "on")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "off");
 			trigger.ProcessMessage("test2", "on");
@@ -102,10 +107,12 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
-			trigger.AddCondition(new Condition(Comparator.Equal, "test2", "on"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "test", "on"),
+				new Condition(Comparator.Equal, "test2", "on")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "off");
 			trigger.ProcessMessage("test2", "off");
@@ -119,10 +126,12 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.All, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
-			trigger.AddCondition(new Condition(Comparator.Equal, "test2", "on"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "test", "on"),
+				new Condition(Comparator.Equal, "test2", "on")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.All, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "on");
 			trigger.ProcessMessage("test2", "off");
@@ -136,10 +145,12 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.All, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
-			trigger.AddCondition(new Condition(Comparator.Equal, "test2", "on"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "test", "on"),
+				new Condition(Comparator.Equal, "test2", "on")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.All, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "off");
 			trigger.ProcessMessage("test2", "off");
@@ -153,10 +164,12 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.All, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
-			trigger.AddCondition(new Condition(Comparator.Equal, "test2", "on"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "test", "on"),
+				new Condition(Comparator.Equal, "test2", "on")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.All, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "on");
 			trigger.ProcessMessage("test2", "on");
@@ -170,10 +183,12 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.All, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.Equal, "solar_event", "civil_twilight_end"));
-			trigger.AddCondition(new Condition(Comparator.Equal, "time", "21:00"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {
+				new Condition(Comparator.Equal, "solar_event", "civil_twilight_end"),
+				new Condition(Comparator.Equal, "time", "21:00")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.All, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("solar_event", "civil_twilight_end");
 			trigger.ProcessMessage("time", "21:00:00");
@@ -187,10 +202,12 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.All, () => triggered = true, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.GreaterThan, "test", "1"));
-			trigger.AddCondition(new Condition(Comparator.LessThan, "test", "3"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {
+				new Condition(Comparator.GreaterThan, "test", "1"),
+				new Condition(Comparator.LessThan, "test", "3")
+			};
+			var trigger = new Trigger(bus.Object, "", TriggerType.All, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "2");
 
@@ -203,9 +220,9 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var count = 0;
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => count++, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.GreaterThan, "test", "0"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => count++) };
+			var conditions = new[] { new Condition(Comparator.GreaterThan, "test", "0") };
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "1");
 			trigger.ProcessMessage("test", "2");
@@ -219,9 +236,9 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var count = 0;
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => count++, logProvider.Object);
-
-			trigger.AddCondition(new Condition(Comparator.GreaterThan, "test", "0"));
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => count++) };
+			var conditions = new[] { new Condition(Comparator.GreaterThan, "test", "0") };
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
 
 			trigger.ProcessMessage("test", "1");
 			trigger.ProcessMessage("test", "0");
@@ -236,15 +253,36 @@ namespace HAServer.Tests.Core {
 			var logProvider = new Mock<ILogProvider>();
 
 			var triggered = false;
-			var trigger = new Trigger(bus.Object, "", TriggerType.Any, () => triggered = true, logProvider.Object) {
+			var actions = new[] { new TriggerAction(TriggerActionType.Device, "device", () => triggered = true) };
+			var conditions = new[] {new Condition(Comparator.Equal, "test", "on")};
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object) {
 				Enabled = false
 			};
 
-			trigger.AddCondition(new Condition(Comparator.Equal, "test", "on"));
-
 			trigger.ProcessMessage("test", "on");
 
 			Assert.That(triggered, Is.False);
 		}
+
+		[Test]
+		public void WhenAnyConditionIsTrue_WithOneConditionMet_ExecuteAllActions() {
+			var bus = new Mock<IMessageBus>();
+			var logProvider = new Mock<ILogProvider>();
+
+			var deviceTriggered = false;
+			var groupTriggered = false;
+			var actions = new[] {
+				new TriggerAction(TriggerActionType.Device, "device", ()=>deviceTriggered=true),
+				new TriggerAction(TriggerActionType.Group, "group", ()=>groupTriggered=true)
+			};
+			var conditions = new[] {new Condition(Comparator.Equal, "test", "on")};
+
+			var trigger = new Trigger(bus.Object, "", TriggerType.Any, actions, conditions, logProvider.Object);
+
+			trigger.ProcessMessage("test", "on");
+
+			Assert.That(deviceTriggered, Is.True);
+			Assert.That(groupTriggered, Is.True);
+		}
 	}
 }

+ 6 - 19
HAServer/Core/Trigger.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Text.RegularExpressions;
-using HAServer.Devices;
 using HAServer.Interfaces.Core;
 
 namespace HAServer.Core {
@@ -16,19 +15,14 @@ namespace HAServer.Core {
 		}
 
 		private readonly IMessageBus _messageBus;
-		private readonly Action _action;
 		private readonly List<Condition> _conditions = new List<Condition>();
 		private readonly Dictionary<Condition, bool> _results = new Dictionary<Condition, bool>();
 		private readonly ILogProvider _logProvider;
 
-		public Trigger(IMessageBus messageBus, string name, TriggerType triggerType, Action action, ILogProvider logProvider)
-			: this(messageBus, name, triggerType, action, Enumerable.Empty<Condition>(), logProvider) {
-		}
-
-		public Trigger(IMessageBus messageBus, string name, TriggerType triggerType, Action action, IEnumerable<Condition> conditions, ILogProvider logProvider) {
+		public Trigger(IMessageBus messageBus, string name, TriggerType triggerType, IEnumerable<TriggerAction> actions, IEnumerable<Condition> conditions, ILogProvider logProvider) {
 			Id = _idCounter++;
 			_messageBus = messageBus;
-			_action = action;
+			Actions = actions;
 			_logProvider = logProvider;
 			Name = name;
 			TriggerType = triggerType;
@@ -40,7 +34,7 @@ namespace HAServer.Core {
 			Enabled = true;
 		}
 
-		public void AddCondition(Condition condition) {
+		private void AddCondition(Condition condition) {
 			_results[condition] = false;
 			_conditions.Add(condition);
 			_messageBus.AddSubscriber(this, $"^{condition.Topic}$");
@@ -77,7 +71,8 @@ namespace HAServer.Core {
 
 			var result = Process(topic, message);
 			if (result) {
-				_action();
+				foreach (var action in Actions)
+					action.Execute();
 				_logProvider.Write($"Trigger '{Name}' has been run", LogLevel.Info);
 			}
 		}
@@ -91,14 +86,6 @@ namespace HAServer.Core {
 		public TriggerType TriggerType { get; }
 		public bool Enabled { get; set; }
 		public IEnumerable<Condition> Conditions => _conditions;
-
-		public string DeviceTopic {
-			get {
-				var device = _action.Target as BaseDevice;
-				return device != null ? device.Topic : string.Empty;
-			}
-		}
-
-		public string DeviceMethod => _action.Method.Name;
+		public IEnumerable<TriggerAction> Actions { get; }
 	}
 }

+ 21 - 0
HAServer/Core/TriggerAction.cs

@@ -0,0 +1,21 @@
+using System;
+
+namespace HAServer.Core {
+	public class TriggerAction {
+		private readonly Action _action;
+
+		public TriggerAction(TriggerActionType type, string identifier, Action action) {
+			Type = type;
+			Identifier = identifier;
+			_action = action;
+		}
+
+		public void Execute() {
+			_action();
+		}
+
+		public TriggerActionType Type { get; }
+		public string Identifier { get; }
+		public string Method => _action.Method.Name;
+	}
+}

+ 6 - 0
HAServer/Core/TriggerActionType.cs

@@ -0,0 +1,6 @@
+namespace HAServer.Core {
+	public enum TriggerActionType {
+		Device,
+		Group
+	}
+}

+ 3 - 0
HAServer/HAServer.csproj

@@ -81,8 +81,11 @@
     <Compile Include="Core\Log.cs" />
     <Compile Include="Core\SettingNotFoundException.cs" />
     <Compile Include="Core\Trigger.cs" />
+    <Compile Include="Core\TriggerAction.cs" />
+    <Compile Include="Core\TriggerActionType.cs" />
     <Compile Include="Core\TriggerConfig.cs" />
     <Compile Include="Core\TriggerType.cs" />
+    <Compile Include="Serialisation\TriggerActionJsonConverter.cs" />
     <Compile Include="Serialisation\DeviceGroupCollectionJsonConverter.cs" />
     <Compile Include="Serialisation\DeviceGroupJsonConverter.cs" />
     <Compile Include="Serialisation\BaseDeviceJsonConverter.cs" />

+ 1 - 0
HAServer/Serialisation/JsonSerialiserSettings.cs

@@ -10,6 +10,7 @@ namespace HAServer.Serialisation {
 			Converters.Add(new DeviceGroupJsonConverter());
 			Converters.Add(new TriggerJsonConverter(messageBus, deviceConfig, logProvider));
 			Converters.Add(new ConditionJsonConverter(deviceConfig));
+			Converters.Add(new TriggerActionJsonConverter(deviceConfig));
 		}
 	}
 }

+ 48 - 0
HAServer/Serialisation/TriggerActionJsonConverter.cs

@@ -0,0 +1,48 @@
+using System;
+using HAServer.Core;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace HAServer.Serialisation {
+	public class TriggerActionJsonConverter:JsonConverter<TriggerAction> {
+		private readonly DeviceConfig _deviceConfig;
+
+		public TriggerActionJsonConverter(DeviceConfig deviceConfig) {
+			_deviceConfig = deviceConfig;
+		}
+
+		public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
+			throw new NotImplementedException();
+		}
+
+		public override bool CanWrite => false;
+
+		public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
+			var jsonObject = JObject.Load(reader);
+			var type = jsonObject["Type"].ToObject<TriggerActionType>();
+			var identifier = jsonObject["Identifier"].ToString();
+			var method = jsonObject["Method"].ToString();
+
+			Action action;
+			switch (type) {
+				case TriggerActionType.Device:
+					if (!_deviceConfig.Devices.ContainsKey(identifier))
+						return null;
+
+					var device = _deviceConfig.Devices[identifier];
+					var deviceMethod = device.GetType().GetMethod(method);
+					action = (Action)Delegate.CreateDelegate(typeof(Action), device, deviceMethod);
+					break;
+				case TriggerActionType.Group:
+					var group = _deviceConfig.Groups[identifier];
+					var groupMethod = group.GetType().GetMethod(method);
+					action = (Action)Delegate.CreateDelegate(typeof(Action), group, groupMethod);
+					break;
+				default:
+					throw new ArgumentOutOfRangeException();
+			}
+
+			return new TriggerAction(type, identifier, action);
+		}
+	}
+}

+ 11 - 3
HAServer/Serialisation/TriggerJsonConverter.cs

@@ -28,6 +28,16 @@ namespace HAServer.Serialisation {
 			var triggerType = jsonObject["TriggerType"].Value<int>();
 			var enabled = jsonObject["Enabled"]?.Value<bool>() ?? true;
 			var conditions = serializer.Deserialize<Condition[]>(jsonObject["Conditions"].CreateReader());
+			var actions = jsonObject["Actions"] != null
+							  ? serializer.Deserialize<TriggerAction[]>(jsonObject["Actions"].CreateReader())
+							  : new[] {GetLegacyAction(jsonObject)};
+
+			return new Trigger(_messageBus, name, (TriggerType)triggerType, actions, conditions, _logProvider) {
+				Enabled = enabled
+			};
+		}
+
+		private TriggerAction GetLegacyAction(JObject jsonObject) {
 			var actionTopic = jsonObject["DeviceTopic"].Value<string>();
 			var actionMethod = jsonObject["DeviceMethod"].Value<string>();
 
@@ -37,9 +47,7 @@ namespace HAServer.Serialisation {
 			var device = _deviceConfig.Devices[actionTopic];
 			var method = device.GetType().GetMethod(actionMethod);
 			var action = (Action)Delegate.CreateDelegate(typeof(Action), device, method);
-			return new Trigger(_messageBus, name, (TriggerType)triggerType, action, conditions, _logProvider) {
-				Enabled = enabled
-			};
+			return new TriggerAction(TriggerActionType.Device, device.Topic, action);
 		}
 	}
 }

+ 0 - 4
HAServer/WebServer/DTO/TriggerDto.cs

@@ -9,8 +9,6 @@ namespace HAServer.WebServer.DTO {
 		public TriggerDto(Trigger trigger) {
 			Id = trigger.Id;
 			Name = trigger.Name;
-			Action = trigger.DeviceMethod;
-			Device = trigger.DeviceTopic;
 			TriggerType = trigger.TriggerType;
 			Conditions = trigger.Conditions.Select(condition => new ConditionDto(condition));
 			Enabled = trigger.Enabled;
@@ -18,8 +16,6 @@ namespace HAServer.WebServer.DTO {
 
 		public int Id { get; }
 		public string Name { get; }
-		public string Action { get; }
-		public string Device { get; }
 		public TriggerType TriggerType { get; }
 		public IEnumerable<ConditionDto> Conditions { get; }
 		public bool Enabled { get; }