static void Main(string[] args)
        {
            BasicConfigurator.Configure();

            //instanciate device manager
            DeviceManager dm = new DeviceManager("192.168.1.200");

            //create alarm automation
            var windowAutomation = new WindowOpenAutomation(dm, AutomationNames.WindowOpenAutomation);

            //create humidty automation group. both humidity sensors and actuators must be assigned to Humidity function and the same rooms
            //actuator has to be in all rooms that should have humidity automation enabled
            var humidityAutomation = new ActuatorSensorAutomation <IHumidityControlDevice>(dm, AutomationNames.HumidityAutomation, Function.Humidity, (a, d) => d.Humidity.Value);

            humidityAutomation.RefencePoint = 55; //you should check humidity across all devices at home, and take mean + 10% to be safe.
            humidityAutomation.Hysteresis   = 5;
            humidityAutomation.MaxOnTime    = new TimeSpan(0, 1, 0);
            humidityAutomation.MinOnTime    = new TimeSpan(0, 0, 15);
            humidityAutomation.MinOffTime   = new TimeSpan(0, 0, 5);

            //create heating automation group. both heating sensors and actuators must be assigned to Heating function and the same rooms
            //actuator has to be in all rooms that should have heating automation enabled
            var heatingAutomation = new ActuatorSensorAutomation <IValveControlDevice>(dm, AutomationNames.HeatingAutomation, Function.Heating, (a, d) => Convert.ToInt32(Math.Round(d.Level.Value * 100)));

            heatingAutomation.RefencePoint = 20; //20% valve open
            heatingAutomation.Hysteresis   = 2;
            heatingAutomation.MaxOnTime    = new TimeSpan(0, 6, 0);
            heatingAutomation.MinOnTime    = new TimeSpan(0, 0, 30);
            heatingAutomation.MinOffTime   = new TimeSpan(0, 5, 0);

            //create master value sync automation, which will sync relevant schedule mastervalues across heating rooms/house.
            var syncHeatingMastervaluesAutomation = new SyncHeatingMasterValuesAutomation(dm, AutomationNames.SyncHeatingMastervaluesAutomation);

            for (;;)
            {
                //this will refresh data from HM api and run all automations
                dm.Work();

                //log events
                List <DatapointEvent> eventsSinceLastRefresh = dm.Refresh();
                foreach (var e in dm.Events)
                {
                    LOGGER.InfoFormat("{0} EVENT {1}: ({2}) => ({3})",
                                      e.EventTimestamp.ToString("o"),
                                      e.Current.Name,
                                      e.Previous == null ? "" : e.Previous.Value, e.Current.Value
                                      );
                }

                //wait a bit before running again
                new ManualResetEvent(false).WaitOne(200);
            }
        }
        static void Main(string[] args)
        {
            log4net.Config.XmlConfigurator.Configure();

            int restartTries = 0;

            for (;;)
            {
                try
                {
                    var dm = new DeviceManager(Settings.Default.HomematicServerAddress);

                    LOGGER.Info("Starting external Slack notification service");
                    Slack s = Slack.TryFromCCU(dm);
                    if (s != null)
                    {
                        dm.RegisterNotificationService(s);
                    }

                    LOGGER.Info("Starting alarm automation");
                    var alarmAutomation = new AlarmAutomation(dm, AutomationNames.AlarmAutomation);

                    LOGGER.Info("Starting humidity automation");
                    var humidityAutomation = new ActuatorSensorAutomation <IHumidityControlDevice>(dm, AutomationNames.HumidityAutomation, Function.Humidity, (a, d) => d.Humidity.Value);
                    humidityAutomation.RefencePoint = Settings.Default.HumidityAutomationRefencePoint;
                    humidityAutomation.Hysteresis   = Settings.Default.HumidityAutomationHysteresis;
                    humidityAutomation.MaxOnTime    = Settings.Default.HumidityAutomationMaxOnTime;
                    humidityAutomation.MinOnTime    = Settings.Default.HumidityAutomationMinOnTime;
                    humidityAutomation.MinOffTime   = Settings.Default.HumidityAutomationMinOffTime;

                    LOGGER.Info("Starting heating automation");
                    var heatingAutomation = new ActuatorSensorAutomation <ITempControlDevice>(dm, AutomationNames.HeatingAutomation, Function.Heating, (a, d) =>
                    {
                        if (d.Boost_Mode.Value)
                        {
                            a.IgnoreLimits = true;
                            return(100);
                        }

                        int target = Convert.ToInt32(Math.Round(d.Set_Point_Temperature.Value * 10M));
                        int actual = Convert.ToInt32(Math.Round(d.Actual_Temperature.Value * 10M));

                        int diff = target - actual;
                        return(diff);
                    }
                                                                                              );
                    heatingAutomation.RefencePoint = Settings.Default.HeatingAutomationRefencePoint;
                    heatingAutomation.Hysteresis   = Settings.Default.HeatingAutomationHysteresis;
                    heatingAutomation.MaxOnTime    = Settings.Default.HeatingAutomationMaxOnTime;
                    heatingAutomation.MinOnTime    = Settings.Default.HeatingAutomationMinOnTime;
                    heatingAutomation.MinOffTime   = Settings.Default.heatingAutomationMinOffTime;

                    LOGGER.Info("Starting window open/close automation");
                    var windowAutomation = new WindowOpenAutomation(dm, AutomationNames.WindowOpenAutomation);

                    LOGGER.Info("Starting heating master valus sync automation");
                    var syncHeatingMastervaluesAutomation = new SyncHeatingMasterValuesAutomation(dm, AutomationNames.SyncHeatingMastervaluesAutomation);

                    LOGGER.Info("Starting webserver");
                    //init web server
                    //web server runs in async mode, locking is required around all DM objects
                    var server = new WebServer(Settings.Default.WebServerListenPort);
                    server.WithCors();

                    RoomController.DeviceManager = dm;
                    server.WithWebApi("/api/room", m => m.WithController <RoomController>());

                    DeviceController.DeviceManager = dm;
                    server.WithWebApi("/api/device", m => m.WithController <DeviceController>());

                    AlarmControler.AlarmAutomation = alarmAutomation;
                    server.WithWebApi("/api/alarm", m => m.WithController <AlarmControler>());

                    server.WithStaticFolder("/", Settings.Default.WebServerRoot, true, m => m.WithContentCaching(true));

                    server.RunAsync();

                    LOGGER.Info("Starting entering main loop");
                    for (;;)
                    {
                        //this will refresh data from HM api and run all automations
                        dm.Work();

                        //log events
                        foreach (var e in dm.Events)
                        {
                            LOGGER.InfoFormat("{0} EVENT {1}: ({2}) => ({3})",
                                              e.EventTimestamp.ToString("o"),
                                              e.Current.Name,
                                              e.Previous == null ? "" : e.Previous.Value, e.Current.Value
                                              );
                        }

                        //refresh data every so often
                        new ManualResetEvent(false).WaitOne(200);

                        restartTries = 0;
                    }
                }
                catch (Exception e)
                {
                    restartTries++;

                    if (restartTries < 5)
                    {
                        LOGGER.Error($"Got unhandled exception {restartTries} in a row. Restarting imediately", e);
                    }
                    else
                    {
                        LOGGER.Error($"Got unhandled exception {restartTries} in a row. Restarting in 3s..", e);
                        new ManualResetEvent(false).WaitOne(3000);
                    }
                }
            }
        }