diff --git a/RGB.NET.Devices.SoIP/Client/SoIPClientDeviceDefinition.cs b/RGB.NET.Devices.SoIP/Client/SoIPClientDeviceDefinition.cs
new file mode 100644
index 0000000..470df74
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Client/SoIPClientDeviceDefinition.cs
@@ -0,0 +1,41 @@
+using RGB.NET.Devices.SoIP.Generic;
+
+namespace RGB.NET.Devices.SoIP.Client
+{
+ public class SoIPClientDeviceDefinition : ISoIPDeviceDefinition
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets or sets the hostname of the device.
+ ///
+ public string Hostname { get; set; }
+
+ ///
+ /// Gets or sets the port to device is listening to.
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// Gets or sets the manufacturer of the device.
+ ///
+ public string Manufacturer { get; set; } = "Unknown";
+
+ ///
+ /// Gets or sets the model name of the device.
+ ///
+ public string Model { get; set; } = "Generic SoIP-Device";
+
+ #endregion
+
+ #region Constructors
+
+ public SoIPClientDeviceDefinition(string hostname, int port)
+ {
+ this.Hostname = hostname;
+ this.Port = port;
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDevice.cs b/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDevice.cs
new file mode 100644
index 0000000..0ba9697
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDevice.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RGB.NET.Core;
+using RGB.NET.Devices.SoIP.Generic;
+using SimpleTCP;
+
+namespace RGB.NET.Devices.SoIP.Client
+{
+ public class SoIPClientRGBDevice : AbstractRGBDevice, ISoIPRGBDevice
+ {
+ #region Properties & Fields
+
+ private readonly Dictionary _syncbackCache = new Dictionary();
+ private readonly SimpleTcpClient _tcpClient;
+
+ public override SoIPClientRGBDeviceInfo DeviceInfo { get; }
+
+ #endregion
+
+ #region Constructors
+
+ public SoIPClientRGBDevice(SoIPClientRGBDeviceInfo deviceInfo)
+ {
+ this.DeviceInfo = deviceInfo;
+
+ _tcpClient = new SimpleTcpClient();
+ _tcpClient.DelimiterDataReceived += TcpClientOnDelimiterDataReceived;
+ }
+
+ #endregion
+
+ #region Methods
+
+ void ISoIPRGBDevice.Initialize(IDeviceUpdateTrigger updateTrigger)
+ {
+ _tcpClient.Connect(DeviceInfo.Hostname, DeviceInfo.Port);
+ }
+
+ ///
+ protected override void UpdateLeds(IEnumerable ledsToUpdate)
+ { }
+
+ ///
+ public override void SyncBack()
+ {
+ lock (_syncbackCache)
+ {
+ foreach (KeyValuePair cacheEntry in _syncbackCache)
+ {
+ LedId ledId = cacheEntry.Key;
+ Color color = cacheEntry.Value;
+
+ if (!LedMapping.TryGetValue(ledId, out Led led))
+ led = InitializeLed(cacheEntry.Key, new Rectangle(0, 0, 10, 10)); //TODO DarthAffe 10.06.2018: Send layout with initial package
+
+ SetLedColorWithoutRequest(led, color);
+ }
+
+ _syncbackCache.Clear();
+ }
+ }
+
+ private void TcpClientOnDelimiterDataReceived(object sender, Message message)
+ {
+ List<(LedId, Color)> leds = message.MessageString.Split(';').Select(x =>
+ {
+ string[] led = x.Split('|');
+ return ((LedId)Enum.Parse(typeof(LedId), led[0]), Color.FromHexString(led[1]));
+ }).ToList();
+ lock (_syncbackCache)
+ foreach ((LedId ledId, Color color) in leds)
+ _syncbackCache[ledId] = color;
+ }
+
+ ///
+ public override void Dispose()
+ {
+ base.Dispose();
+
+ _tcpClient.Disconnect();
+ _tcpClient.Dispose();
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDeviceInfo.cs b/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDeviceInfo.cs
new file mode 100644
index 0000000..a878d09
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDeviceInfo.cs
@@ -0,0 +1,56 @@
+using System;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.SoIP.Client
+{
+ ///
+ ///
+ /// Represents device information for a />.
+ ///
+ public class SoIPClientRGBDeviceInfo : IRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ public RGBDeviceType DeviceType => RGBDeviceType.Unknown;
+
+ ///
+ public string Manufacturer { get; }
+
+ ///
+ public string Model { get; }
+
+ ///
+ public RGBDeviceLighting Lighting => RGBDeviceLighting.None;
+
+ ///
+ public bool SupportsSyncBack => true;
+
+ ///
+ public Uri Image { get; set; }
+
+ ///
+ /// The hostname of the device.
+ ///
+ public string Hostname { get; }
+
+ ///
+ /// The port of the device.
+ ///
+ public int Port { get; }
+
+ #endregion
+
+ #region Constructors
+
+ internal SoIPClientRGBDeviceInfo(SoIPClientDeviceDefinition deviceDefinition)
+ {
+ this.Manufacturer = deviceDefinition.Manufacturer;
+ this.Model = deviceDefinition.Model;
+ this.Hostname = deviceDefinition.Hostname;
+ this.Port = deviceDefinition.Port;
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.SoIP/FodyWeavers.xml b/RGB.NET.Devices.SoIP/FodyWeavers.xml
new file mode 100644
index 0000000..ba40f91
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/FodyWeavers.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/RGB.NET.Devices.SoIP/Generic/ISoIPDeviceDefinition.cs b/RGB.NET.Devices.SoIP/Generic/ISoIPDeviceDefinition.cs
new file mode 100644
index 0000000..5c6dba8
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Generic/ISoIPDeviceDefinition.cs
@@ -0,0 +1,8 @@
+namespace RGB.NET.Devices.SoIP.Generic
+{
+ ///
+ /// Marker interface for SoIP device definitions.
+ ///
+ public interface ISoIPDeviceDefinition
+ { }
+}
diff --git a/RGB.NET.Devices.SoIP/Generic/ISoIPRGBDevice.cs b/RGB.NET.Devices.SoIP/Generic/ISoIPRGBDevice.cs
new file mode 100644
index 0000000..d7d6743
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Generic/ISoIPRGBDevice.cs
@@ -0,0 +1,11 @@
+using System;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.SoIP.Generic
+{
+ // ReSharper disable once InconsistentNaming
+ internal interface ISoIPRGBDevice : IRGBDevice, IDisposable
+ {
+ void Initialize(IDeviceUpdateTrigger updateTrigger);
+ }
+}
diff --git a/RGB.NET.Devices.SoIP/RGB.NET.Devices.SoIP.csproj b/RGB.NET.Devices.SoIP/RGB.NET.Devices.SoIP.csproj
new file mode 100644
index 0000000..16247f6
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/RGB.NET.Devices.SoIP.csproj
@@ -0,0 +1,74 @@
+
+
+ netstandard2.0;net45
+ win7-x86;win7-x64
+
+ Darth Affe
+ Wyrez
+ en-US
+ en-US
+ RGB.NET.Devices.SoIP
+ RGB.NET.Devices.SoIP
+ RGB.NET.Devices.SoIP
+ RGB.NET.Devices.SoIP
+ RGB.NET.Devices.SoIP
+ SoIP-Device-Implementations of RGB.NET
+ SoIP-Device-Implementations of RGB.NET, a C# (.NET) library
+ Copyright © Wyrez 2017
+ Copyright © Wyrez 2017
+ http://lib.arge.be/icon.png
+ https://github.com/DarthAffe/RGB.NET
+ https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE
+ Github
+ https://github.com/DarthAffe/RGB.NET
+ True
+
+
+
+ 0.0.1
+ 0.0.1
+ 0.0.1
+
+ ..\bin\
+ true
+ True
+ True
+ latest
+
+
+
+ TRACE;NETCORE;NETSTANDARD;NETSTANDARD2_0;DEBUG;NETSTANDARD2_0
+
+
+
+ NET45;NETFULL
+
+
+
+ TRACE;DEBUG
+ true
+ full
+ false
+
+
+
+ pdbonly
+ true
+ $(NoWarn);CS1591;CS1572;CS1573
+ RELEASE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RGB.NET.Devices.SoIP/Server/SoIPServerDeviceDefinition.cs b/RGB.NET.Devices.SoIP/Server/SoIPServerDeviceDefinition.cs
new file mode 100644
index 0000000..2eff475
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Server/SoIPServerDeviceDefinition.cs
@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using System.Linq;
+using RGB.NET.Core;
+using RGB.NET.Devices.SoIP.Generic;
+
+namespace RGB.NET.Devices.SoIP.Server
+{
+ public class SoIPServerDeviceDefinition : ISoIPDeviceDefinition
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets or sets the port to device is listening to.
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// Gets or sets the manufacturer of the device.
+ ///
+ public string Manufacturer { get; set; } = "Unknown";
+
+ ///
+ /// Gets or sets the model name of the device.
+ ///
+ public string Model { get; set; } = "Generic SoIP-Device";
+
+ ///
+ /// Gets the IDs of the leds represented by this device.
+ ///
+ public List Leds { get; }
+
+ #endregion
+
+ #region Constructors
+
+ public SoIPServerDeviceDefinition(int port, params LedId[] ledIds)
+ {
+ this.Port = port;
+ this.Leds = ledIds.ToList();
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDevice.cs b/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDevice.cs
new file mode 100644
index 0000000..79c20a1
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDevice.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using RGB.NET.Core;
+using RGB.NET.Devices.SoIP.Generic;
+using SimpleTCP;
+
+namespace RGB.NET.Devices.SoIP.Server
+{
+ public class SoIPServerRGBDevice : AbstractRGBDevice, ISoIPRGBDevice
+ {
+ #region Properties & Fields
+
+ private readonly List _leds;
+ private readonly SimpleTcpServer _tcpServer;
+ private SoIPServerUpdateQueue _updateQueue;
+
+ public override SoIPServerRGBDeviceInfo DeviceInfo { get; }
+
+ #endregion
+
+ #region Constructors
+
+ public SoIPServerRGBDevice(SoIPServerRGBDeviceInfo deviceInfo, List leds)
+ {
+ this.DeviceInfo = deviceInfo;
+ this._leds = leds;
+
+ _tcpServer = new SimpleTcpServer();
+ _tcpServer.ClientConnected += TcpServerOnClientConnected;
+ }
+
+ #endregion
+
+ #region Methods
+
+ void ISoIPRGBDevice.Initialize(IDeviceUpdateTrigger updateTrigger)
+ {
+ int count = 0;
+ foreach (LedId id in _leds)
+ InitializeLed(id, new Rectangle((count++) * 10, 0, 10, 10));
+
+ //TODO DarthAffe 10.06.2018: Allow to load a layout.
+
+ if (Size == Size.Invalid)
+ {
+ Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle));
+ Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
+ }
+
+ _tcpServer.Start(DeviceInfo.Port);
+ _updateQueue = new SoIPServerUpdateQueue(updateTrigger, _tcpServer);
+ }
+
+ protected override void UpdateLeds(IEnumerable ledsToUpdate) => _updateQueue.SetData(ledsToUpdate);
+
+ private void TcpServerOnClientConnected(object sender, TcpClient tcpClient)
+ {
+ string message = GetLedString(LedMapping.Values);
+ byte[] messageData = _tcpServer.StringEncoder.GetBytes(message + _tcpServer.StringEncoder.GetString(new[] { _tcpServer.Delimiter }));
+ tcpClient.GetStream().WriteAsync(messageData, 0, messageData.Length);
+ }
+
+ private string GetLedString(IEnumerable leds) => string.Join(";", leds.Select(x => x.Id.ToString() + "|" + x.Color.AsARGBHexString()));
+
+ ///
+ public override void Dispose()
+ {
+ base.Dispose();
+
+ _tcpServer.Stop();
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDeviceInfo.cs b/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDeviceInfo.cs
new file mode 100644
index 0000000..25db97e
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDeviceInfo.cs
@@ -0,0 +1,50 @@
+using System;
+using RGB.NET.Core;
+
+namespace RGB.NET.Devices.SoIP.Server
+{
+ ///
+ ///
+ /// Represents device information for a />.
+ ///
+ public class SoIPServerRGBDeviceInfo : IRGBDeviceInfo
+ {
+ #region Properties & Fields
+
+ ///
+ public RGBDeviceType DeviceType => RGBDeviceType.Unknown;
+
+ ///
+ public string Manufacturer { get; }
+
+ ///
+ public string Model { get; }
+
+ ///
+ public RGBDeviceLighting Lighting => RGBDeviceLighting.Key;
+
+ ///
+ public bool SupportsSyncBack => false;
+
+ ///
+ public Uri Image { get; set; }
+
+ ///
+ /// The port of the device.
+ ///
+ public int Port { get; }
+
+ #endregion
+
+ #region Constructors
+
+ internal SoIPServerRGBDeviceInfo(SoIPServerDeviceDefinition deviceDefinition)
+ {
+ this.Manufacturer = deviceDefinition.Manufacturer;
+ this.Model = deviceDefinition.Model;
+ this.Port = deviceDefinition.Port;
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Devices.SoIP/Server/SoIPServerUpdateQueue.cs b/RGB.NET.Devices.SoIP/Server/SoIPServerUpdateQueue.cs
new file mode 100644
index 0000000..5fac483
--- /dev/null
+++ b/RGB.NET.Devices.SoIP/Server/SoIPServerUpdateQueue.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RGB.NET.Core;
+using SimpleTCP;
+
+namespace RGB.NET.Devices.SoIP.Server
+{
+ ///
+ ///
+ /// Represents the update-queue performing updates for E131-DMX devices.
+ ///
+ public class SoIPServerUpdateQueue : UpdateQueue
+ {
+ #region Properties & Fields
+
+ private readonly SimpleTcpServer _tcpServer;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The update trigger used by this queue.
+ /// The hostname of the device this queue is performing updates for.
+ public SoIPServerUpdateQueue(IDeviceUpdateTrigger updateTrigger, SimpleTcpServer tcpServer)
+ : base(updateTrigger)
+ {
+ this._tcpServer = tcpServer;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ protected override void Update(Dictionary