diff --git a/Brematic.Net.sln b/Brematic.Net.sln
new file mode 100644
index 0000000..779d322
--- /dev/null
+++ b/Brematic.Net.sln
@@ -0,0 +1,45 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28010.2041
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brematic.Net", "src\Brematic.Net\Brematic.Net.csproj", "{AB55CB3E-977B-4101-9419-13F866F8D6D2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C4A1C06F-9D6D-40C5-95A2-18D2B129618B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brematic.Net.Tests", "tests\Brematic.Net.Tests\Brematic.Net.Tests.csproj", "{65E659DB-34CD-4961-990F-2463DE6B096B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{CC115A2C-A9E5-44AC-8002-8E0E5309C130}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brematic.Net.ConsoleSample", "samples\Brematic.Net.ConsoleSample\Brematic.Net.ConsoleSample.csproj", "{D5A85DD6-403B-4192-832C-C091D7E99BB9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AB55CB3E-977B-4101-9419-13F866F8D6D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AB55CB3E-977B-4101-9419-13F866F8D6D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AB55CB3E-977B-4101-9419-13F866F8D6D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AB55CB3E-977B-4101-9419-13F866F8D6D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65E659DB-34CD-4961-990F-2463DE6B096B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65E659DB-34CD-4961-990F-2463DE6B096B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65E659DB-34CD-4961-990F-2463DE6B096B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65E659DB-34CD-4961-990F-2463DE6B096B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D5A85DD6-403B-4192-832C-C091D7E99BB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5A85DD6-403B-4192-832C-C091D7E99BB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D5A85DD6-403B-4192-832C-C091D7E99BB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5A85DD6-403B-4192-832C-C091D7E99BB9}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {65E659DB-34CD-4961-990F-2463DE6B096B} = {C4A1C06F-9D6D-40C5-95A2-18D2B129618B}
+ {D5A85DD6-403B-4192-832C-C091D7E99BB9} = {CC115A2C-A9E5-44AC-8002-8E0E5309C130}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {80F2A93D-9921-4E99-984F-B43C220A64A5}
+ EndGlobalSection
+EndGlobal
diff --git a/samples/Brematic.Net.ConsoleSample/Brematic.Net.ConsoleSample.csproj b/samples/Brematic.Net.ConsoleSample/Brematic.Net.ConsoleSample.csproj
new file mode 100644
index 0000000..23e0012
--- /dev/null
+++ b/samples/Brematic.Net.ConsoleSample/Brematic.Net.ConsoleSample.csproj
@@ -0,0 +1,12 @@
+
+
+
+ Exe
+ netcoreapp2.1
+
+
+
+
+
+
+
diff --git a/samples/Brematic.Net.ConsoleSample/Program.cs b/samples/Brematic.Net.ConsoleSample/Program.cs
new file mode 100644
index 0000000..7940918
--- /dev/null
+++ b/samples/Brematic.Net.ConsoleSample/Program.cs
@@ -0,0 +1,21 @@
+using Brematic.Net.Devices;
+using Brematic.Net.Devices.Brennenstuhl;
+using Brematic.Net.Gateways;
+
+namespace Brematic.Net.ConsoleSample
+{
+ internal class Program
+ {
+ private static void Main(string[] args)
+ {
+ var systemCode = "01010";
+ var unitCode = "00010";
+
+ var device = new RCS1000N(systemCode, unitCode);
+
+ var gateway = new BrennenstuhlGateway("192.168.178.213");
+
+ gateway.SendRequest(device, DeviceAction.On);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Brematic.Net.csproj b/src/Brematic.Net/Brematic.Net.csproj
new file mode 100644
index 0000000..d9baf23
--- /dev/null
+++ b/src/Brematic.Net/Brematic.Net.csproj
@@ -0,0 +1,12 @@
+
+
+
+ netstandard2.0
+ Tobias Efinger
+ 0.0.1
+ https://github.com/tefinger/Brematic.Net
+ https://github.com/tefinger/Brematic.Net
+ brematic intertechno brennenstuhl smarthome
+
+
+
diff --git a/src/Brematic.Net/Devices/Brennenstuhl/RCR1000N.cs b/src/Brematic.Net/Devices/Brennenstuhl/RCR1000N.cs
new file mode 100644
index 0000000..5833b1c
--- /dev/null
+++ b/src/Brematic.Net/Devices/Brennenstuhl/RCR1000N.cs
@@ -0,0 +1,7 @@
+namespace Brematic.Net.Devices.Brennenstuhl
+{
+ public class RCR1000N : RCS1000N
+ {
+ public RCR1000N(string systemCode, string unitCode) : base(systemCode, unitCode) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Devices/Brennenstuhl/RCS1000N.cs b/src/Brematic.Net/Devices/Brennenstuhl/RCS1000N.cs
new file mode 100644
index 0000000..1afa11f
--- /dev/null
+++ b/src/Brematic.Net/Devices/Brennenstuhl/RCS1000N.cs
@@ -0,0 +1,83 @@
+using Brematic.Net.Exceptions;
+using Brematic.Net.Gateways;
+
+namespace Brematic.Net.Devices.Brennenstuhl
+{
+ public class RCS1000N : Device
+ {
+ private const int Repeat = 10;
+ private const int PauseBS = 5600;
+ private const int PauseIT = 11200;
+ private const int Tune = 350;
+ private const int BaudBS = 25;
+ private const int BaudIT = 26;
+ private const int SpeedBS = 16;
+ private const int SpeedIT = 32;
+ private const int TxVersion = 1;
+
+ private const string Low = "1,";
+ private const string High = "3,";
+
+ private const string SeqLow = Low + High + Low + High;
+ private const string SeqHigh = Low + High + High + Low;
+
+ private const string Additional = SeqLow + SeqHigh;
+ private const string On = SeqLow + SeqHigh;
+ private const string Off = SeqHigh + SeqLow;
+
+ public RCS1000N(string systemCode, string unitCode) : base(systemCode, unitCode) { }
+
+ private string HeadBSGW => $"TXP:0,0,{Repeat},{PauseBS},{Tune},{BaudBS},";
+ private string TailBSGW => $"{TxVersion},{SpeedBS};";
+ private string HeadITGW => $"0,0,{Repeat},{PauseIT},{Tune},{BaudIT},0,";
+ private string TailITGW => $"{TxVersion},{SpeedIT},0";
+
+ private string Encode(string code)
+ {
+ var encodedMsg = "";
+ foreach (var c in code)
+ switch (c)
+ {
+ case '0':
+ encodedMsg += SeqHigh;
+ break;
+ case '1':
+ encodedMsg += SeqLow;
+ break;
+ default:
+ throw new InvalidCodeException();
+ }
+
+ return encodedMsg;
+ }
+
+ public override string GetSignal(Gateway gateway, DeviceAction action)
+ {
+ var systemMsg = Encode(SystemCode);
+ var unitMsg = Encode(UnitCode);
+
+ string head;
+ string tail;
+
+ if (gateway.GetType() == typeof(BrennenstuhlGateway))
+ {
+ head = HeadBSGW;
+ tail = TailBSGW;
+ }
+ else if (gateway.GetType() == typeof(IntertechnoGateway))
+ {
+ head = HeadITGW;
+ tail = TailITGW;
+ }
+ else
+ {
+ throw new UnsupportedGatewayException();
+ }
+
+ var actionValue = action == DeviceAction.On ? On : Off;
+ var data = head + systemMsg + unitMsg + actionValue + tail;
+
+ return data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Devices/Device.cs b/src/Brematic.Net/Devices/Device.cs
new file mode 100644
index 0000000..e4a8e6f
--- /dev/null
+++ b/src/Brematic.Net/Devices/Device.cs
@@ -0,0 +1,19 @@
+using Brematic.Net.Gateways;
+
+namespace Brematic.Net.Devices
+{
+ public abstract class Device
+ {
+ protected Device(string systemCode, string unitCode)
+ {
+ SystemCode = systemCode;
+ UnitCode = unitCode;
+ }
+
+ protected string SystemCode { get; }
+
+ protected string UnitCode { get; }
+
+ public abstract string GetSignal(Gateway gateway, DeviceAction action);
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Devices/DeviceAction.cs b/src/Brematic.Net/Devices/DeviceAction.cs
new file mode 100644
index 0000000..b4413e2
--- /dev/null
+++ b/src/Brematic.Net/Devices/DeviceAction.cs
@@ -0,0 +1,8 @@
+namespace Brematic.Net.Devices
+{
+ public enum DeviceAction
+ {
+ On,
+ Off
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Exceptions/InvalidCodeException.cs b/src/Brematic.Net/Exceptions/InvalidCodeException.cs
new file mode 100644
index 0000000..e51c4a4
--- /dev/null
+++ b/src/Brematic.Net/Exceptions/InvalidCodeException.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Brematic.Net.Exceptions
+{
+ public class InvalidCodeException : Exception
+ {
+ public InvalidCodeException() : base("Invalid value in system or unit code!") { }
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Exceptions/UnsupportedGatewayException.cs b/src/Brematic.Net/Exceptions/UnsupportedGatewayException.cs
new file mode 100644
index 0000000..72bf489
--- /dev/null
+++ b/src/Brematic.Net/Exceptions/UnsupportedGatewayException.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Brematic.Net.Exceptions
+{
+ public class UnsupportedGatewayException : Exception
+ {
+ public UnsupportedGatewayException() : base("Unsupported gateway") { }
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Gateways/BrennenstuhlGateway.cs b/src/Brematic.Net/Gateways/BrennenstuhlGateway.cs
new file mode 100644
index 0000000..7932025
--- /dev/null
+++ b/src/Brematic.Net/Gateways/BrennenstuhlGateway.cs
@@ -0,0 +1,11 @@
+using Brematic.Net.Devices;
+
+namespace Brematic.Net.Gateways
+{
+ public class BrennenstuhlGateway : Gateway
+ {
+ public BrennenstuhlGateway(string ipAddress) : this(ipAddress, 49880) { }
+
+ public BrennenstuhlGateway(string ipAddress, int port) : base(ipAddress, port) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Gateways/Gateway.cs b/src/Brematic.Net/Gateways/Gateway.cs
new file mode 100644
index 0000000..77d78db
--- /dev/null
+++ b/src/Brematic.Net/Gateways/Gateway.cs
@@ -0,0 +1,51 @@
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using Brematic.Net.Devices;
+
+namespace Brematic.Net.Gateways
+{
+ public abstract class Gateway
+ {
+ protected Gateway(string ipAddress, int port)
+ {
+ IPAddress = ipAddress;
+ Port = port;
+ }
+
+ private string IPAddress { get; }
+
+ private int Port { get; }
+
+ public void SendRequest(Device device, DeviceAction action)
+ {
+ var data = device.GetSignal(this, action);
+ SendDataToSocket(data);
+ }
+
+ private Socket ConnectSocket()
+ {
+ var hostEntry = Dns.GetHostEntry(IPAddress);
+
+ foreach (var address in hostEntry.AddressList)
+ {
+ var ipEndPoint = new IPEndPoint(address, Port);
+ var tempSocket = new Socket(SocketType.Dgram, ProtocolType.Udp);
+ tempSocket.Connect(ipEndPoint);
+
+ if (tempSocket.Connected) return tempSocket;
+ }
+
+ return null;
+ }
+
+ protected void SendDataToSocket(string data)
+ {
+ var byteData = Encoding.UTF8.GetBytes(data);
+ using (var socket = ConnectSocket())
+ {
+ socket.Send(byteData);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Brematic.Net/Gateways/IntertechnoGateway.cs b/src/Brematic.Net/Gateways/IntertechnoGateway.cs
new file mode 100644
index 0000000..ea8dc81
--- /dev/null
+++ b/src/Brematic.Net/Gateways/IntertechnoGateway.cs
@@ -0,0 +1,12 @@
+using System;
+using Brematic.Net.Devices;
+
+namespace Brematic.Net.Gateways
+{
+ public class IntertechnoGateway : Gateway
+ {
+ public IntertechnoGateway(string ipAddress) : this(ipAddress, 49880) { }
+
+ public IntertechnoGateway(string ipAddress, int port) : base(ipAddress, port) { }
+ }
+}
\ No newline at end of file
diff --git a/tests/Brematic.Net.Tests/Brematic.Net.Tests.csproj b/tests/Brematic.Net.Tests/Brematic.Net.Tests.csproj
new file mode 100644
index 0000000..b7e2fc4
--- /dev/null
+++ b/tests/Brematic.Net.Tests/Brematic.Net.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netcoreapp2.1;net471
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
+
+
+
+
diff --git a/tests/Brematic.Net.Tests/Devices/Brennenstuhl/BrennenstuhlTests.cs b/tests/Brematic.Net.Tests/Devices/Brennenstuhl/BrennenstuhlTests.cs
new file mode 100644
index 0000000..74e7f24
--- /dev/null
+++ b/tests/Brematic.Net.Tests/Devices/Brennenstuhl/BrennenstuhlTests.cs
@@ -0,0 +1,63 @@
+using Brematic.Net.Devices;
+using Brematic.Net.Devices.Brennenstuhl;
+using Brematic.Net.Gateways;
+using Xunit;
+
+namespace Brematic.Net.Tests.Devices.Brennenstuhl
+{
+ public class BrennenstuhlTests
+ {
+ private readonly BrennenstuhlGateway _brennenstuhlGateway;
+ private readonly IntertechnoGateway _intertechnoGateway;
+
+ public BrennenstuhlTests()
+ {
+ _brennenstuhlGateway = new BrennenstuhlGateway("192.168.178.213");
+ _intertechnoGateway = new IntertechnoGateway("192.68.178.213");
+ }
+
+ [Theory]
+ [InlineData(DeviceAction.On,
+ "TXP:0,0,10,5600,350,25,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,16;",
+ "0,0,10,11200,350,26,0,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,32,0"
+ )]
+ [InlineData(DeviceAction.Off,
+ "TXP:0,0,10,5600,350,25,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,16;",
+ "0,0,10,11200,350,26,0,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,32,0")]
+ public void RCS1000N_Test(DeviceAction action, string expectedResultBS, string expectedResultIT)
+ {
+ // given
+ var device = new RCS1000N("10000", "00100");
+
+ // when
+ var resultBS = device.GetSignal(_brennenstuhlGateway, action);
+ var resultIT = device.GetSignal(_intertechnoGateway, action);
+
+ // then
+ Assert.Equal(expectedResultBS, resultBS);
+ Assert.Equal(expectedResultIT, resultIT);
+ }
+
+ [Theory]
+ [InlineData(DeviceAction.On,
+ "TXP:0,0,10,5600,350,25,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,16;",
+ "0,0,10,11200,350,26,0,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,32,0"
+ )]
+ [InlineData(DeviceAction.Off,
+ "TXP:0,0,10,5600,350,25,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,16;",
+ "0,0,10,11200,350,26,0,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,32,0")]
+ public void RCR1000N_Test(DeviceAction action, string expectedResultBS, string expectedResultIT)
+ {
+ // given
+ var device = new RCR1000N("10000", "00100");
+
+ // when
+ var resultBS = device.GetSignal(_brennenstuhlGateway, action);
+ var resultIT = device.GetSignal(_intertechnoGateway, action);
+
+ // then
+ Assert.Equal(expectedResultBS, resultBS);
+ Assert.Equal(expectedResultIT, resultIT);
+ }
+ }
+}
\ No newline at end of file