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)); }
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 })); }
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)); }
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)); }
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)); }
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()); }
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")); }
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); }
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)); }