Dr. Peter Trimmel
Published © MIT

Home Control Web

ASP.NET Web application to integrate various devices such as a home control system, ventilation unit, power meter, PV, heating boiler...

IntermediateProtip3,133
Home Control Web

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Raspberry Pi 2 Model B
Raspberry Pi 2 Model B
×1

Software apps and online services

Windows 10 IoT Core
Microsoft Windows 10 IoT Core
Microsoft Visual Studio 2017 Community Edition
Microsoft ASP.NET Core 2.0
ThingSpeak API
ThingSpeak API

Story

Read more

Schematics

Overview

Picture2 lcbzv0d52v

Code

Program.cs

C#
The main application entry for the ASP.NET web application running the web host.
namespace HomeControlWeb
{
    using System;
    using System.IO;
    using System.Net;
    using System.Security.Cryptography.X509Certificates;

    using Microsoft.AspNetCore;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
    using Microsoft.Extensions.Configuration;
    using Serilog;

    using HomeControlWeb.Models;

    /// <summary>
    /// Main application class providing the main entry point <see cref="Main(string[])"/>.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// This is the main entry point to the application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Starting web host...");
                var host = BuildWebHost(args);
                Log.Information("Starting web host");
                host.Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
                Console.WriteLine($"Web host terminated with exception: {ex.Message}.");
            }
            finally
            {
                Log.Information("Web host terminated");
                Log.CloseAndFlush();
                Console.WriteLine("Web host terminated.");
            }
        }

        /// <summary>
        /// Creates a host using an instance of WebHostBuilder using the <see cref="Startup"/> class.
        /// Note that using HTTPS is configured for the kestrel web server and a Serilog file logger
        /// is configured using rolling files in the Log subdirectory.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        /// <returns>The web host.</returns>
        public static IWebHost BuildWebHost(string[] args)
        {
            // Getting configuration and app settings.
            var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .AddEnvironmentVariables()
                .AddCommandLine(args)
                .Build();

            AppSettings settings = new AppSettings();
            config.GetSection("AppSettings").Bind(settings);

            // Configure logger.
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(config)
                .Enrich.FromLogContext()
                .WriteTo.Debug()
                .CreateLogger();

            // Setup web host.
            return WebHost.CreateDefaultBuilder(args)
                .UseConfiguration(config)
                .UseSerilog()
                .UseKestrel(options =>
                {
                    var listen = settings.Listen;

                    // Run callbacks on the transport thread
                    options.ApplicationSchedulingMode = SchedulingMode.Inline;

                    if (string.IsNullOrEmpty(listen.Certificate))
                    {
                        // Setup HTTP
                        options.Listen(IPAddress.Any, listen.BasePort);
                    }
                    else
                    {
                        var certificate = new X509Certificate2(listen.Certificate, listen.Password, X509KeyStorageFlags.MachineKeySet);

                        // Setup HTTPS
                        options.Listen(IPAddress.Any, listen.BasePort, listenOptions =>
                        {
                            listenOptions.NoDelay = false;
                            listenOptions.UseHttps(certificate);
                        });
                    }
                })
                .UseStartup<Startup>()
                .Build();
        }
    }
}

Startup.cs

C#
Configuration of the ASP.NET application and services.
namespace HomeControlWeb
{
    using System;
    using System.IO;
    using System.Net;

    using Microsoft.ApplicationInsights.Extensibility;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc.Infrastructure;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Routing;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.PlatformAbstractions;

    using HomeControlWeb.Data;
    using HomeControlWeb.Models;
    using HomeControlWeb.Services;
    using HomeControlWeb.Swagger;
    using HomeControlWeb.Extensions;
    using HomeControlWeb.Hubs;

    /// <summary>
    /// The Startup class configures services and the application's request pipeline.
    /// </summary>
    public class Startup
    {
        /// <summary>
        /// The Startup class constructor accepts dependencies that are provided through dependency injection.
        /// </summary>
        /// <param name="configuration">The configuration instance.</param>
        public Startup(IConfiguration configuration)
        {
            ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
            Configuration = configuration;
        }

        /// <summary>
        /// The configuration property.
        /// </summary>
        public IConfiguration Configuration { get; }

        /// <summary>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// It is called before the Configure method by the web host.
        /// </summary>
        /// <param name="services">The service collection.</param>
        public void ConfigureServices(IServiceCollection services)
        {
            // Get application settings.
            AppSettings settings = new AppSettings();
            Configuration.GetSection("AppSettings").Bind(settings);

            // Add SQLite database for identity service.
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));

            // Configure identity (user, password, validation).
            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.Configure<IdentityOptions>(options =>
            {
                // Password settings
                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 8;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = true;
                options.Password.RequireLowercase = false;
                options.Password.RequiredUniqueChars = 6;

                // Lockout settings
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
                options.Lockout.MaxFailedAccessAttempts = 10;
                options.Lockout.AllowedForNewUsers = true;

                // User settings
                options.User.RequireUniqueEmail = true;
            });

            services.ConfigureApplicationCookie(options =>
            {
                // Cookie settings
                options.Cookie.HttpOnly = true;
                options.Cookie.Expiration = TimeSpan.FromDays(150);
                options.SlidingExpiration = true;
            });

            // Includes support for Razor Pages and controllers.
            services.AddMvc()
                .AddRazorPagesOptions(options =>
                {
                    options.Conventions.AuthorizeFolder("/Account/Manage");
                    options.Conventions.AuthorizePage("/Account/Logout");
                })
                .AddJsonOptions(options =>
                {
                    options.SerializerSettings.Formatting = Formatting.Indented;
                });

            // Required for handling of AntiForgeryToken.
            services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

            // Required to use the Options<T> pattern for application settings.
            services.AddOptions();
            services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

            // Add application services for sending emails.
            services.AddSingleton<IEmailSender, EmailSender>();
            services.Configure<SenderOptions>(Configuration);

            // Required to use paging for large datasets.
            services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
            services.AddScoped<IUrlHelper>(factory =>
            {
                var actionContext = factory.GetService<IActionContextAccessor>().ActionContext;
                return new UrlHelper(actionContext);
            });

            // Adding SignalR support.
            services.AddSignalR();

            // Adding additional services.
            services.AddSingleton<PAC3200Provider>();
            services.AddSingleton<KWLEC200Provider>();
            services.AddSingleton<ETAPU11Provider>();
            services.AddSingleton<FroniusProvider>();
            services.AddSingleton<NetatmoProvider>();
            services.AddSingleton<ZipatoProvider>();
            services.AddSingleton<OverviewProvider>();
            services.AddSingleton<ThingSpeakProvider>();
            services.AddSingleton<IHostedService, PAC3200Monitor>();
            services.AddSingleton<IHostedService, KWLEC200Monitor>();
            services.AddSingleton<IHostedService, ETAPU11Monitor>();
            services.AddSingleton<IHostedService, FroniusMonitor>();
            services.AddSingleton<IHostedService, NetatmoMonitor>();
            services.AddSingleton<IHostedService, ZipatoMonitor>();
            services.AddSingleton<IHostedService, ThingSpeakMonitor>();

            // Add Swagger support using the application settings.
            services.AddSwaggerGen(c =>
            {
                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "HomeControlWeb.xml");
                var swagger = settings.Swagger;

                c.SwaggerDoc(swagger.Overview.Version, swagger.Overview);
                c.SwaggerDoc(swagger.ETAPU11.Version, swagger.ETAPU11);
                c.SwaggerDoc(swagger.PAC3200.Version, swagger.PAC3200);
                c.SwaggerDoc(swagger.KWLEC200.Version, swagger.KWLEC200);
                c.SwaggerDoc(swagger.Fronius.Version, swagger.Fronius);
                c.SwaggerDoc(swagger.Netatmo.Version, swagger.Netatmo);
                c.SwaggerDoc(swagger.Zipato.Version, swagger.Zipato);

                c.IncludeXmlComments(xmlPath);
                c.DescribeAllEnumsAsStrings();
                c.DescribeStringEnumsInCamelCase();
                c.OperationFilter<DataAnnotationAttributesOperationFilter>();
            });
        }

        /// <summary>
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// The request pipeline consists of a sequence of request delegates, called one after the other.
        /// </summary>
        /// <param name="app">The IApplicationBuilder instance.</param>
        /// <param name="env">The IHostingEnvironment instance.</param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            // Get application settings.
            AppSettings settings = new AppSettings();
            Configuration.GetSection("AppSettings").Bind(settings);

            // Setup error handling.
            if (env.IsDevelopment())
            {
                TelemetryConfiguration.Active.DisableTelemetry = true;
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            // Use static files and authentication.
            app.UseStaticFiles();
            app.UseAuthentication();

            // Use default routing to controller actions.
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action=Index}/{id?}");
            });

            // Use SignalR and setup the various hubs.
            app.UseSignalR(router =>
            {
                router.MapHub<OverviewHub>("OverviewHub");
                router.MapHub<ETAPU11Hub>("ETAPU11Hub");
                router.MapHub<PAC3200Hub>("PAC3200Hub");
                router.MapHub<KWLEC200Hub>("KWLEC200Hub");
                router.MapHub<FroniusHub>("FroniusHub");
                router.MapHub<NetatmoHub>("NetatmoHub");
                router.MapHub<ZipatoHub>("ZipatoHub");
            });

            // Use Swagger and setup Swagger UI.
            app.UseSwaggerAuthorized();
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                var swagger = settings.Swagger;

                c.SwaggerEndpoint($"/swagger/{swagger.Overview.Version}/swagger.json", $"{swagger.Overview.Title} - {swagger.Overview.Version}");
                c.SwaggerEndpoint($"/swagger/{swagger.ETAPU11.Version}/swagger.json", $"{swagger.ETAPU11.Title} - {swagger.ETAPU11.Version}");
                c.SwaggerEndpoint($"/swagger/{swagger.PAC3200.Version}/swagger.json", $"{swagger.PAC3200.Title} - {swagger.PAC3200.Version}");
                c.SwaggerEndpoint($"/swagger/{swagger.KWLEC200.Version}/swagger.json", $"{swagger.KWLEC200.Title} - {swagger.KWLEC200.Version}");
                c.SwaggerEndpoint($"/swagger/{swagger.Fronius.Version}/swagger.json", $"{swagger.Fronius.Title} - {swagger.Fronius.Version}");
                c.SwaggerEndpoint($"/swagger/{swagger.Netatmo.Version}/swagger.json", $"{swagger.Netatmo.Title} - {swagger.Netatmo.Version}");
                c.SwaggerEndpoint($"/swagger/{swagger.Zipato.Version}/swagger.json", $"{swagger.Zipato.Title} - {swagger.Zipato.Version}");

                c.DisplayRequestDuration();
                c.DocExpansion(DocExpansion.List);
                c.EnableFilter();
                c.DefaultModelRendering(ModelRendering.Example);

                c.ValidatorUrl(null);
                c.SupportedSubmitMethods(SubmitMethod.Get, SubmitMethod.Put, SubmitMethod.Post);
            });
        }
    }
}

HomeControlWeb.csproj

XML
Example for the project file showing the settings and dependencies.
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <RuntimeIdentifiers>win-arm;win-x64</RuntimeIdentifiers>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DocumentationFile>homecontrolweb.xml</DocumentationFile>
    <NoWarn>1701;1702;1705;1591</NoWarn>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <DocumentationFile>homecontrolweb.xml</DocumentationFile>
    <NoWarn>1701;1702;1705;1591</NoWarn>
  </PropertyGroup>
  <ItemGroup>
    <None Update="HomeControlWeb.db" CopyToOutputDirectory="PreserveNewest" />
    <None Update="HomeControlWeb.pfx" CopyToOutputDirectory="PreserveNewest" />
    <None Update="homecontrolweb.xml" CopyToOutputDirectory="PreserveNewest" />
    <None Remove="Properties\PublishProfiles\FolderProfile.pubxml" />
    <None Remove="Properties\PublishProfiles\FolderProfile1.pubxml" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="ETAPU11Lib" Version="1.0.5" />
    <PackageReference Include="FroniusLib" Version="2.0.4" />
    <PackageReference Include="KWLEC200Lib" Version="2.0.6" />
    <PackageReference Include="MailKit" Version="2.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-alpha2-final" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.0.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" PrivateAssets="All" />
    <PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.2" PrivateAssets="All" />
    <PackageReference Include="NetatmoLib" Version="2.0.7" />
    <PackageReference Include="PAC3200Lib" Version="2.0.7" />
    <PackageReference Include="Serilog.AspNetCore" Version="2.1.0" />
    <PackageReference Include="Serilog.Settings.Configuration" Version="2.5.0" />
    <PackageReference Include="Serilog.Sinks.ColoredConsole" Version="3.0.1" />
    <PackageReference Include="Serilog.Sinks.Debug" Version="1.0.0" />
    <PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="2.0.0" />
    <PackageReference Include="Syncfusion.Compression.NETStandard20" Version="15.4200.0.20" />
    <PackageReference Include="Syncfusion.EJ.Pivot" Version="15.4600.0.20" />
    <PackageReference Include="Syncfusion.EJ.AspNet.Core" Version="15.4600.0.20" />
    <PackageReference Include="ThingSpeakLib" Version="2.0.4" />
    <PackageReference Include="ZipatoLib" Version="2.0.8" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Logs\" />
  </ItemGroup>
  <ItemGroup>
    <Content Update="appsettings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</Project>

Credits

Dr. Peter Trimmel

Dr. Peter Trimmel

1 project • 1 follower
Programming and playing with computers for more than 40 years....
Contact

Comments

Add projectSign up / Login