Exemple #1
0
        public async Task InvokeAsync(HttpContext context)
        {
            var config = await ConfigStore.GetConfigAsync();

            if (!ClientFactory.IsInitialized)
            {
                InitializeOrReset(config);

                if (!ClientFactory.IsInitialized)
                {
                    // If we aren't on one of the approved pages, redirect to the settings page and prompt for setup.
                    if (context.Request.Path.ToString().ToLower() != "/admin/settings" &&
                        context.Request.Path.ToString().ToLower() != "/admin/pagemigration" &&
                        context.Request.Path.ToString().ToLower() != "/admin")
                    {
                        Log.LogInformation($"HA connection is not initialized, redirecting user to settings area...");

                        context.Response.Redirect("/admin/settings?setup=1");
                        return;
                    }
                }
            }

            // Pages Migration
            await MigrateLegacyPagesConfig(context, config);

            await Next(context);
        }
        public async Task <IActionResult> DeletePageConfirmation([FromRoute] string page)
        {
            var cfg = await ConfigStore.GetConfigAsync();

            var p = cfg[page];

            if (cfg.Pages.Count == 1)
            {
                TempData.AddWarning("You must have at least one page. Please create a new page before attempting to delete this one.");
                return(RedirectToAction("Index"));
            }

            if (p == null)
            {
                TempData.AddWarning($"The specified page '{page}' does not exist in the configuration.");
                return(RedirectToAction("Index"));
            }

            if (p.IsDefaultPage)
            {
                TempData.AddWarning($"You can't delete the default page. Assign another page as the default first, then delete this one.");
                return(RedirectToAction("Index"));
            }

            return(View(p));
        }
        public async Task <IActionResult> ImportTheme([FromForm] IFormFile file)
        {
            if (file == null)
            {
                TempData.AddWarning("No file was uploaded. Please try again.");
                return(RedirectToAction("Themes"));
            }

            string contents;

            using (var sr = new StreamReader(file.OpenReadStream()))
            {
                contents = await sr.ReadToEndAsync();
            }

            try
            {
                var newTheme = JsonConvert.DeserializeObject <Theme>(contents);

                var config = await ConfigStore.GetConfigAsync();

                await ConfigStore.ManipulateConfig(c => c.CurrentTheme = newTheme);

                TempData.AddSuccess($"Successfully imported theme file '{file.FileName}' successfully! <a href=\"/\">Go check out your dashboard!</a>");
            }
            catch
            {
                TempData.AddError("Import file was not a theme file or could not otherwise be imported. Check that the file is not malformed and try again.");
            }

            return(RedirectToAction("Themes"));
        }
        public async Task <IActionResult> DeleteTile([FromQuery] string name)
        {
            var config = await ConfigStore.GetConfigAsync();

            var tile = config.Tiles.FirstOrDefault(t => t.Name == name);

            if (tile == null)
            {
                TempData.AddError($"Could not delete tile with name '{name}' (name not found).");
                return(RedirectToAction("Index"));
            }

            config.Tiles.Remove(tile);

            var layout = config.TileLayout.FirstOrDefault(l => l.Name == name);

            if (layout != null)
            {
                config.TileLayout.Remove(layout);
            }

            await ConfigStore.SaveConfigAsync(config);

            TempData.AddSuccess($"Tile '{name}' was deleted successfully.");

            return(RedirectToAction("Index"));
        }
        public async Task <IActionResult> Settings()
        {
            var config = await ConfigStore.GetConfigAsync();

            try
            {
                if (TempData["check-settings"] is bool b && b)
                {
                    // This doesn't check if the access token is valid...
                    var inst = await DiscoveryClient.GetDiscoveryInfo();

                    // ... but this does.
                    var entities = await EntityClient.GetEntities();

                    ViewBag.Instance = $"Home Assistant instance: <b>{inst.LocationName} (Version {inst.Version}) [{inst.BaseUrl}]</b>";
                }
            }
            catch (Exception ex)
            {
                await ConfigStore.ManipulateConfig(c => c.Settings.AccessToken = c.Settings.BaseUri = null);

                config = await ConfigStore.GetConfigAsync();

                TempData.Remove(AlertManager.GRP_SUCCESS);
                Logger.LogError(ex, "Invalid system settings entered, or unable to reach Home Assistant with the specified information.");
                TempData.AddError("The settings entered are not valid. HACC is unable to reach your Home Assistant instance. Try your entries again, consult the <a target=\"_blank\" href=\"https://github.com/qJake/HADotNet.CommandCenter/wiki/Initial-System-Setup\">setup guide</a> for help, or check the logs (console) for more information.");

                // Reloads the request showing the error (TempData doesn't commit until a reload).
                return(RedirectToAction("Settings"));
            }

            return(View(config.Settings));
        }
Exemple #6
0
        public async Task <IActionResult> Add()
        {
            ViewBag.Entities      = (await EntityClient.GetEntities("calendar")).OrderBy(e => e).Select(e => new SelectListItem(e, e));
            ViewBag.CurrentConfig = await ConfigStore.GetConfigAsync();

            return(View());
        }
        public async Task <IActionResult> AddPage([FromForm] Page newPage)
        {
            if (ModelState.IsValid)
            {
                var defaultLayout = (await ConfigStore.GetConfigAsync()).Pages.FirstOrDefault(p => p.IsDefaultPage)?.LayoutSettings;
                if (newPage.IsDefaultPage)
                {
                    await ConfigStore.ManipulateConfig(c => c.Pages.ForEach(p => p.IsDefaultPage = false));
                }

                newPage.TileLayout     = new List <TileLayout>();
                newPage.Tiles          = new List <BaseTile>();
                newPage.LayoutSettings = new LayoutSettings
                {
                    BaseTileSizePx = defaultLayout?.BaseTileSizePx ?? 100,
                    TileSpacingPx  = defaultLayout?.TileSpacingPx ?? 6,
                    DeviceHeightPx = defaultLayout?.DeviceHeightPx ?? 860,
                    DeviceWidthPx  = defaultLayout?.DeviceWidthPx ?? 965
                };

                await ConfigStore.ManipulateConfig(c => c.Pages.Add(newPage));

                TempData.AddSuccess($"Successfully added page '{newPage.Description}'!");
                return(RedirectToAction("Index"));
            }
            return(View());
        }
        public async Task InvokeAsync(HttpContext context)
        {
            var config = await ConfigStore.GetConfigAsync();

            if (!string.IsNullOrWhiteSpace(config?.Settings?.BaseUri) && !string.IsNullOrWhiteSpace(config?.Settings?.AccessToken))
            {
                if (!ClientFactory.IsInitialized)
                {
                    Log.LogInformation($"Initializing HA Client Factory with URL {config?.Settings?.BaseUri ?? "[NULL]"} and access token [{new string(config?.Settings?.AccessToken.Take(6).ToArray())}•••••••••••{new string(config?.Settings?.AccessToken.TakeLast(6).ToArray())}].");
                }

                ClientFactory.Initialize(config.Settings.BaseUri, config.Settings.AccessToken);
            }

            if (!ClientFactory.IsInitialized)
            {
                if (context.Request.Path.ToString().ToLower() != "/admin/settings")
                {
                    Log.LogInformation($"Client factory is not initialized, redirecting user to settings area...");

                    context.Response.Redirect("/admin/settings?setup=1");
                    return;
                }
            }

            await Next(context);
        }
        public async Task <IActionResult> Add()
        {
            var config = await ConfigStore.GetConfigAsync();

            ViewBag.Pages = config.Pages.Select(p => new SelectListItem($"{p.Description} ({p.Name})", p.Name));

            return(View());
        }
        public async Task <IActionResult> Edit([FromRoute] string page, string name)
        {
            var config = await ConfigStore.GetConfigAsync();

            var tile = config[page].Tiles.FirstOrDefault(t => t.Name == name);

            return(View("Add", tile));
        }
        public async Task <IActionResult> EditTile([FromQuery] string name)
        {
            var config = await ConfigStore.GetConfigAsync();

            var tile = config.Tiles.FirstOrDefault(t => t.Name == name);

            return(RedirectToAction("Edit", tile.TypeProper + "Tile", new { name }));
        }
Exemple #12
0
        public async Task InvokeAsync(HttpContext context)
        {
            var config = await ConfigStore.GetConfigAsync();

            if (!string.IsNullOrWhiteSpace(config?.Settings?.BaseUri) && (!string.IsNullOrWhiteSpace(config?.Settings?.AccessToken) || !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("HASSIO_TOKEN"))))
            {
                if (!ClientFactory.IsInitialized)
                {
                    if (config.Settings.IsHassIo)
                    {
                        Log.LogInformation($"Auto-initializing HACC via Hass.io addon.");
                        ClientFactory.Initialize(config.Settings.BaseUri, Environment.GetEnvironmentVariable("HASSIO_TOKEN"));
                    }
                    else
                    {
                        Log.LogInformation($"Initializing HACC API with URL {config?.Settings?.BaseUri ?? "[NULL]"} and access token [{new string(config?.Settings?.AccessToken.Take(6).ToArray())}•••••••••••{new string(config?.Settings?.AccessToken.TakeLast(6).ToArray())}].");
                        ClientFactory.Initialize(config.Settings.BaseUri, config.Settings.AccessToken);
                    }
                }
            }
            else
            {
                ClientFactory.Reset();
            }

            if (!ClientFactory.IsInitialized)
            {
                // If we're in Hass.io mode, set the base URI and redirect to the admin homepage.
                if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("HASSIO_TOKEN")))
                {
                    await ConfigStore.ManipulateConfig(c =>
                    {
                        c.Settings = new SystemSettings
                        {
                            BaseUri     = "http://hassio/homeassistant",
                            AccessToken = null,
                            IsHassIo    = true
                        };

                        context.Response.StatusCode = 303;
                        context.Response.Redirect("/admin");
                    });
                }

                // Otherwise, if we aren't on one of the approved pages, redirect to the settings page and prompt for setup.
                if (context.Request.Path.ToString().ToLower() != "/admin/settings" && context.Request.Path.ToString().ToLower() != "/admin")
                {
                    Log.LogInformation($"Client factory is not initialized, redirecting user to settings area...");

                    context.Response.Redirect("/admin/settings?setup=1");
                    return;
                }
            }

            await Next(context);
        }
        public async Task <IActionResult> Edit([FromRoute] string page, string name)
        {
            var config = await ConfigStore.GetConfigAsync();

            ViewBag.Pages = config.Pages.Select(p => new SelectListItem($"{p.Description} ({p.Name})", p.Name));

            var tile = config[page].Tiles.FirstOrDefault(t => t.Name == name);

            return(View("Add", tile));
        }
Exemple #14
0
        public async Task <IActionResult> Edit([FromQuery] string name)
        {
            await PopulateSelectLists();

            var config = await ConfigStore.GetConfigAsync();

            var tile = config.Tiles.FirstOrDefault(t => t.Name == name);

            return(View("Add", tile));
        }
        public async Task <IActionResult> Edit([FromQuery] string name)
        {
            var config = await ConfigStore.GetConfigAsync();

            var tile = config.Tiles.FirstOrDefault(t => t.Name == name);

            ViewBag.Entities = (await EntityClient.GetEntities("scene")).OrderBy(e => e).Select(e => new SelectListItem(e, e));

            return(View("Add", tile));
        }
Exemple #16
0
        public async Task <IActionResult> Save([FromRoute] string page, CalendarTile tile)
        {
            if (ModelState.IsValid)
            {
                return(await SaveBaseTile(page, ConfigStore, tile));
            }

            ViewBag.Entities      = (await EntityClient.GetEntities("calendar")).OrderBy(e => e).Select(e => new SelectListItem(e, e));
            ViewBag.CurrentConfig = await ConfigStore.GetConfigAsync();

            return(View("Add", tile));
        }
        public async Task <IActionResult> EditPage([FromRoute] string page)
        {
            var cfg = await ConfigStore.GetConfigAsync();

            var p = cfg[page];

            if (p == null)
            {
                TempData.AddWarning($"The specified page '{page}' does not exist in the configuration.");
                return(RedirectToAction("Index"));
            }

            return(View(p));
        }
Exemple #18
0
        public async Task <IActionResult> ExportTheme()
        {
            var config = await ConfigStore.GetConfigAsync();

            var theme     = JsonConvert.SerializeObject(config.CurrentTheme, Formatting.Indented);
            var themeData = Encoding.UTF8.GetBytes(theme);

            Response.ContentType = "application/octet-stream";
            Response.Headers["Content-Disposition"]       = "attachment; filename=hacc-export.theme.json";
            Response.Headers["Content-Transfer-Encoding"] = "binary";
            Response.Body.Write(themeData, 0, themeData.Length);
            Response.Body.Flush();

            return(Ok());
        }
Exemple #19
0
        public async Task <IActionResult> SaveTheme([FromForm] Theme newTheme)
        {
            if (ModelState.IsValid)
            {
                var config = await ConfigStore.GetConfigAsync();

                await ConfigStore.ManipulateConfig(c => c.CurrentTheme = newTheme);

                TempData.AddSuccess("Saved theme settings successfully!");

                return(RedirectToAction("Themes"));
            }

            return(View("Themes", newTheme));
        }
        public async Task <IActionResult> ImportConfig([FromForm] IFormFile file)
        {
            try
            {
                if (file == null)
                {
                    TempData.AddWarning("No file was uploaded. Please try again.");
                    return(RedirectToAction("Index"));
                }

                string contents;
                using (var sr = new StreamReader(file.OpenReadStream()))
                {
                    contents = await sr.ReadToEndAsync();
                }

                try
                {
                    var newConfig = JsonConvert.DeserializeObject <ConfigRoot>(contents);
                    newConfig.Settings = null;

                    var config = await ConfigStore.GetConfigAsync();

                    var oldSettings = config.Settings;

                    newConfig.Settings = oldSettings;

                    await ConfigStore.SaveConfigAsync(newConfig);

                    // Ensures arrays are non-null even if no actions are performed.
                    await ConfigStore.ManipulateConfig();

                    TempData.AddSuccess($"Successfully imported system configuration file '{file.FileName}'!");
                }
                catch
                {
                    Logger.LogWarning("File selected was not able to be parsed into a config object. Check file contents and try again.");
                    TempData.AddError("Import file was not a configuration file or could not otherwise be imported. Check that the file is not malformed and try again.");
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Importing system config settings failed.");
                TempData.AddError("Unable to import system configuration. See log output (console) for more information.");
            }
            return(RedirectToAction("Index"));
        }
        public async Task <IActionResult> Layout()
        {
            var config = await ConfigStore.GetConfigAsync();

            ViewBag.PreviewWidth   = config.LayoutSettings?.DeviceWidthPx / 2.0;
            ViewBag.PreviewHeight  = config.LayoutSettings?.DeviceHeightPx / 2.0;
            ViewBag.PreviewSize    = config.LayoutSettings?.BaseTileSizePx / 2.0;
            ViewBag.Padding        = config.LayoutSettings?.TileSpacingPx;
            ViewBag.PreviewPadding = config.LayoutSettings?.TileSpacingPx / 2.0;

            return(View(from t in config.Tiles
                        join layout in config.TileLayout on t.Name equals layout.Name into tileGroup
                        from l in tileGroup.DefaultIfEmpty(null)
                        select new TileWithLayoutViewModel
            {
                Tile = t,
                Layout = l,
                Settings = config.LayoutSettings
            }));
        }
        public async Task <IActionResult> SaveTheme([FromForm] Theme newTheme)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    var config = await ConfigStore.GetConfigAsync();

                    await ConfigStore.ManipulateConfig(c => c.CurrentTheme = newTheme);

                    TempData.AddSuccess("Saved theme settings successfully!");

                    return(RedirectToAction("Themes"));
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Saving theme settings failed.");
                TempData.AddError("Unable to save theme changes. See log output (console) for more information.");
            }
            return(View("Themes", newTheme));
        }
        public async Task <IActionResult> ImportTheme([FromForm] IFormFile file)
        {
            try
            {
                if (file == null)
                {
                    TempData.AddWarning("No file was uploaded. Please try again.");
                    return(RedirectToAction("Themes"));
                }

                string contents;
                using (var sr = new StreamReader(file.OpenReadStream()))
                {
                    contents = await sr.ReadToEndAsync();
                }

                try
                {
                    var newTheme = JsonConvert.DeserializeObject <Theme>(contents);

                    var config = await ConfigStore.GetConfigAsync();

                    await ConfigStore.ManipulateConfig(c => c.CurrentTheme = newTheme);

                    TempData.AddSuccess($"Successfully imported theme file '{file.FileName}' successfully! <a href=\"/d\" target=\"_blank\">Go check out your dashboard!</a>");
                }
                catch
                {
                    Logger.LogWarning("File selected was not able to be parsed into a theme. Check file contents and try again.");
                    TempData.AddError("Import file was not a theme file or could not otherwise be imported. Check that the file is not malformed and try again.");
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Importing theme settings failed.");
                TempData.AddError("Unable to import theme. See log output (console) for more information.");
            }
            return(RedirectToAction("Themes"));
        }
        public async Task <IActionResult> ExportConfig()
        {
            var config = await ConfigStore.GetConfigAsync();

            config.Settings = null;

            var configJson = JsonConvert.SerializeObject(config, JsonConfigStore.SerializerSettings);
            var configData = Encoding.UTF8.GetBytes(configJson);
            var filename   = $"hacc-export-{DateTime.Now:yyyyMMdd-HHmmss}.config.json";

            Response.ContentType = "application/octet-stream";
            Response.Headers["Content-Disposition"]       = $"attachment; filename={filename}";
            Response.Headers["Content-Transfer-Encoding"] = "binary";
            await Response.Body.WriteAsync(configData, 0, configData.Length);

            Response.Body.Flush();

            TempData.AddWarning("WARNING: Be careful when importing this file into another HACC instance. If the names of Home Assistant entities are different on the target platform, HACC may experience errors!");

            Logger.LogInformation($"Exported system configuration to downloaded file '{filename}'.");

            return(Ok());
        }
        public async Task <IActionResult> DeletePage([FromRoute] string page)
        {
            var cfg = await ConfigStore.GetConfigAsync();

            var p = cfg[page];

            if (cfg.Pages.Count == 1 || (p?.IsDefaultPage ?? false))
            {
                TempData.AddWarning("You have to have at least one page, and you can't delete the default page.");
                return(RedirectToAction("Index"));
            }

            if (p == null)
            {
                TempData.AddWarning($"The specified page '{page}' does not exist in the configuration.");
                return(RedirectToAction("Index"));
            }

            await ConfigStore.ManipulateConfig(c => c.Pages.Remove(c[page]));

            TempData.AddSuccess($"The page '{page}' was successfully deleted.");

            return(RedirectToAction("Index"));
        }
Exemple #26
0
        public async Task InvokeAsync(HttpContext context)
        {
            var config = await ConfigStore.GetConfigAsync();

            if (!ClientFactory.IsInitialized)
            {
                InitializeOrReset(config);

                if (!ClientFactory.IsInitialized)
                {
                    // If we're in Hass.io mode, set the base URI and redirect to the admin homepage.
                    if (SupervisorEnvironment.IsSupervisorAddon)
                    {
                        await AttemptBaseUrlDiscovery();

                        InitializeOrReset(config, true);
                    }

                    // If we aren't on one of the approved pages, redirect to the settings page and prompt for setup.
                    if (context.Request.Path.ToString().ToLower() != "/admin/settings" &&
                        context.Request.Path.ToString().ToLower() != "/admin/pagemigration" &&
                        context.Request.Path.ToString().ToLower() != "/admin")
                    {
                        Log.LogInformation($"HA connection is not initialized, redirecting user to settings area...");

                        context.Response.Redirect("/admin/settings?setup=1");
                        return;
                    }
                }
            }

            // Pages Migration
            await MigrateLegacyPagesConfig(context, config);

            await Next(context);
        }
        public async Task <IActionResult> Index()
        {
            var config = await ConfigStore.GetConfigAsync();

            return(View(config.Tiles));
        }
        public async Task InvokeAsync(HttpContext context)
        {
            var config = await ConfigStore.GetConfigAsync();

            if (!string.IsNullOrWhiteSpace(config?.Settings?.BaseUri) && (!string.IsNullOrWhiteSpace(config?.Settings?.AccessToken) || !string.IsNullOrWhiteSpace(SupervisorEnvironment.GetSupervisorToken())))
            {
                if (!ClientFactory.IsInitialized)
                {
                    if (config.Settings.IsHassIo)
                    {
                        Log.LogInformation($"Auto-initializing HACC via Hass.io addon.");
                        ClientFactory.Initialize(config.Settings.BaseUri, SupervisorEnvironment.GetSupervisorToken());

                        var discovery = ClientFactory.GetClient <DiscoveryClient>();
                        var discInfo  = await discovery.GetDiscoveryInfo();

                        await ConfigStore.ManipulateConfig(c => c.Settings.ExternalBaseUri = discInfo.BaseUrl);
                    }
                    else
                    {
                        Log.LogInformation($"Initializing HACC API with URL {config?.Settings?.BaseUri ?? "[NULL]"} and access token [{new string(config?.Settings?.AccessToken.Take(6).ToArray())}•••••••••••{new string(config?.Settings?.AccessToken.TakeLast(6).ToArray())}].");
                        ClientFactory.Initialize(config.Settings.BaseUri, config.Settings.AccessToken);
                    }
                }
            }
            else
            {
                ClientFactory.Reset();
            }

            if (!ClientFactory.IsInitialized)
            {
                // If we're in Hass.io mode, set the base URI and redirect to the admin homepage.
                if (!string.IsNullOrWhiteSpace(SupervisorEnvironment.GetSupervisorToken()))
                {
                    await ConfigStore.ManipulateConfig(c =>
                    {
                        c.Settings = new SystemSettings
                        {
                            BaseUri     = "http://hassio/homeassistant",
                            AccessToken = null,
                            IsHassIo    = true
                        };

                        context.Response.StatusCode = 303;
                        context.Response.Redirect("/admin");
                    });
                }

                // Otherwise, if we aren't on one of the approved pages, redirect to the settings page and prompt for setup.
                if (context.Request.Path.ToString().ToLower() != "/admin/settings" && context.Request.Path.ToString().ToLower() != "/admin")
                {
                    Log.LogInformation($"Client factory is not initialized, redirecting user to settings area...");

                    context.Response.Redirect("/admin/settings?setup=1");
                    return;
                }
            }

            // Pages Migration

#pragma warning disable CS0612
            if ((config.TileLayout?.Count > 0 || config.Tiles?.Count > 0) && (config.Pages?.Count ?? 0) == 0)
            {
                await ConfigStore.ManipulateConfig(config =>
                {
                    config.Pages ??= new List <Page>();
                    config.Pages.Add(new Page
                    {
                        Name           = "default",
                        Description    = "[Automatically generated from previous configuration.]",
                        IsDefaultPage  = true,
                        Tiles          = config.Tiles,
                        TileLayout     = config.TileLayout,
                        LayoutSettings = config.LayoutSettings
                    });
                    config.TileLayout     = null;
                    config.Tiles          = null;
                    config.LayoutSettings = null;
                });

                context.Response.Redirect("/admin/pageMigration");
            }
            else if (config.Pages.Count == 0)
            {
                await ConfigStore.ManipulateConfig(config =>
                {
                    config.Pages = new List <Page>
                    {
                        new Page
                        {
                            Name           = "default",
                            Description    = "Default Page",
                            IsDefaultPage  = true,
                            Tiles          = new List <BaseTile>(),
                            TileLayout     = new List <TileLayout>(),
                            LayoutSettings = new LayoutSettings
                            {
                                DeviceHeightPx = 1280,
                                DeviceWidthPx  = 720,
                                BaseTileSizePx = 92,
                                TileSpacingPx  = 6,
                            }
                        }
                    };
                });
            }
#pragma warning restore CS0612

            await Next(context);
        }
Exemple #29
0
        public async Task <IActionResult> Settings()
        {
            var config = await ConfigStore.GetConfigAsync();

            return(View(config.Settings));
        }
        public async Task <IActionResult> Themes()
        {
            var config = await ConfigStore.GetConfigAsync();

            return(View(config.CurrentTheme));
        }