diff --git a/AppBroker.App/Controller/LayoutController.cs b/AppBroker.App/Controller/LayoutController.cs index 681631e..8c5d174 100644 --- a/AppBroker.App/Controller/LayoutController.cs +++ b/AppBroker.App/Controller/LayoutController.cs @@ -17,7 +17,8 @@ namespace AppBroker.App.Controller; public record LayoutRequest(string TypeName, string IconName, long DeviceId); -public record LayoutResponse(DeviceLayout? Layout, SvgIcon? Icon); +public record IconResponse(SvgIcon Icon, string Name); +public record LayoutResponse(DeviceLayout? Layout, IconResponse? Icon, IconResponse[] additionalIcons); [Route("app/layout")] @@ -37,9 +38,31 @@ public LayoutResponse GetSingle([FromQuery] LayoutRequest request) { var layout = GetLayout(request); var icon = GetIcon(request, layout?.IconName); + var additional = GetAdditionalIcons(layout); - return new LayoutResponse(layout, icon); + return new LayoutResponse(layout, icon, additional); } + + private IconResponse[] GetAdditionalIcons(DeviceLayout? layout) + { + var histIconNames = + (layout?.DetailDeviceLayout?.HistoryProperties?.Select(x => x?.IconName) ?? Array.Empty()) + .Concat(layout?.DetailDeviceLayout?.TabInfos?.Select(x=>x.IconName) ?? Array.Empty()) + .Distinct(); + var additional = Array.Empty(); + if (histIconNames is not null) + { + additional = histIconNames + .Where(x => x is not null) + .Select(x => GetIcon(null, x)) + .Where(x => x is not null) + .Select(x => x!) + .ToArray(); + } + + return additional; + } + [HttpGet("multi")] public List GetMultiple([FromQuery] List request) { @@ -50,7 +73,7 @@ public List GetMultiple([FromQuery] List request) var layout = GetLayout(req); var icon = GetIcon(req, layout?.IconName); if (icon is not null || layout is not null) - response.Add(new LayoutResponse(layout, icon)); + response.Add(new LayoutResponse(layout, icon, GetAdditionalIcons(layout))); } return response; } @@ -58,10 +81,23 @@ public List GetMultiple([FromQuery] List request) [HttpGet("all")] public List GetAll() { - return DeviceLayoutService + try + { + return DeviceLayoutService .GetAllLayouts() - .Select(x => new LayoutResponse(x, GetIcon(null, x.IconName))) - .ToList(); + .Select(x => new LayoutResponse(x, GetIcon(null, x.IconName), GetAdditionalIcons(x))) + .ToList(); + } + catch (Exception) + { + throw; + } + } + + [HttpGet("iconByName")] + public SvgIcon GetAllIcons(string name) + { + return iconService.GetIconByName(name); } private DeviceLayout? GetLayout(LayoutRequest request) @@ -85,25 +121,32 @@ public List GetAll() return layout; } - private SvgIcon? GetIcon(LayoutRequest? request, string? iconName) + private IconResponse? GetIcon(LayoutRequest? request, string? iconName) { + IconResponse? res = null; SvgIcon? icon = null; if (!string.IsNullOrWhiteSpace(iconName)) { icon = iconService.GetIconByName(iconName); + if (icon is not null) + res = new IconResponse(icon, iconName); } if (request is not null) { if (icon is null && !string.IsNullOrWhiteSpace(request?.IconName)) { icon = iconService.GetIconByName(request.IconName); + if (icon is not null) + res = new IconResponse(icon, request!.IconName); } if (icon is null && !string.IsNullOrWhiteSpace(request?.TypeName)) { icon = iconService.GetBestFitIcon(request.TypeName); + if (icon is not null) + res = new IconResponse(icon, request!.TypeName); } } - return icon; + return res; } [HttpPatch] diff --git a/AppBroker.App/Controller/SmarthomeController.cs b/AppBroker.App/Controller/SmarthomeController.cs index 2b5e83f..a093f3f 100644 --- a/AppBroker.App/Controller/SmarthomeController.cs +++ b/AppBroker.App/Controller/SmarthomeController.cs @@ -25,10 +25,10 @@ public class SmarthomeController : ControllerBase private readonly IDeviceManager deviceManager; private readonly NLog.ILogger logger; - public SmarthomeController(IDeviceManager deviceManager, ILogger logger) + public SmarthomeController(IDeviceManager deviceManager) { this.deviceManager = deviceManager; - this.logger = logger; + logger = LogManager.LogFactory.GetCurrentClassLogger(); } /// diff --git a/AppBroker.App/Plugin.cs b/AppBroker.App/Plugin.cs index ecc8544..179bb57 100644 --- a/AppBroker.App/Plugin.cs +++ b/AppBroker.App/Plugin.cs @@ -20,7 +20,7 @@ internal class Plugin : IPlugin { public string Name => "App"; - public int LoadOrder => int.MinValue; + public int LoadOrder => 0; public bool Initialize(LogFactory logFactory) { diff --git a/AppBroker.Core/AppBroker.Core.csproj b/AppBroker.Core/AppBroker.Core.csproj index 2ffb770..0b7cd39 100644 --- a/AppBroker.Core/AppBroker.Core.csproj +++ b/AppBroker.Core/AppBroker.Core.csproj @@ -19,6 +19,8 @@ + + @@ -26,7 +28,7 @@ - + diff --git a/AppBroker.Core/BasicMessage.cs b/AppBroker.Core/BasicMessage.cs index 2ceb0cf..7cc31f6 100644 --- a/AppBroker.Core/BasicMessage.cs +++ b/AppBroker.Core/BasicMessage.cs @@ -3,6 +3,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using System.ComponentModel; + namespace AppBroker.Core; public abstract class BaseSmarthomeMessage diff --git a/AppBroker.Core/Configuration/DatabaseConfig.cs b/AppBroker.Core/Configuration/DatabaseConfig.cs index 5afc68e..89c216d 100644 --- a/AppBroker.Core/Configuration/DatabaseConfig.cs +++ b/AppBroker.Core/Configuration/DatabaseConfig.cs @@ -17,8 +17,6 @@ public DatabaseConfig() HistoryDBConnectionString = "Data Source=history.db"; BrokerDatabasePluginName = "NonSucking.Framework.Extension.Database.Sqlite.dll"; HistoryDatabasePluginName = "NonSucking.Framework.Extension.Database.Sqlite.dll"; - - } } diff --git a/AppBroker.Core/DynamicUI/DeviceLayoutService.cs b/AppBroker.Core/DynamicUI/DeviceLayoutService.cs index 774baf6..4e81cb2 100644 --- a/AppBroker.Core/DynamicUI/DeviceLayoutService.cs +++ b/AppBroker.Core/DynamicUI/DeviceLayoutService.cs @@ -1,6 +1,12 @@ -using Newtonsoft.Json; +using Json.Path; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using System.Diagnostics; using System.Security.Cryptography; +using System.Text; +using System.Text.Json.Nodes; namespace AppBroker.Core.DynamicUI; @@ -29,7 +35,7 @@ static DeviceLayoutService() } #pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms - private static string GetMD5StringFor(byte[] bytes) + private static string GetMD5StringFor(ReadOnlySpan bytes) { Span toWriteBytes = stackalloc byte[16]; _ = MD5.HashData(bytes, toWriteBytes); @@ -37,17 +43,17 @@ private static string GetMD5StringFor(byte[] bytes) } #pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms - private static DeviceLayout GetLayout(string path) + private static DeviceLayout GetLayout(string fileName, JToken rawData) { - var text = File.ReadAllText(path); - var hash = GetMD5StringFor(File.ReadAllBytes(path)); - var layout = JsonConvert.DeserializeObject(text); - if(layout is null) + var hash = GetMD5StringFor(Encoding.UTF8.GetBytes(rawData.ToString())); + + var layout = rawData.ToObject(); + if (layout is null) { - throw new FileLoadException($"Could not parse {text} to DeviceLayout"); + throw new FileLoadException($"Could not parse {fileName} to DeviceLayout"); } - return layout with { AdditionalDataDes = layout.AdditionalDataDes ?? [], Hash= hash }; - + return layout with { AdditionalDataDes = layout.AdditionalDataDes ?? [], Hash = hash }; + } private static void FileChanged(object sender, FileSystemEventArgs e) { @@ -56,7 +62,10 @@ private static void FileChanged(object sender, FileSystemEventArgs e) _ = Task.Delay(100).ContinueWith((t) => { logger.Info($"Layout change detected: {e.FullPath}"); - var layout = GetLayout(e.FullPath); + + var (name, token) = GetLayoutsWithPathReplaced("DeviceLayouts").FirstOrDefault(x => x.Key == e.Name); + + var layout = GetLayout(name, token); if (layout is null) return; @@ -137,17 +146,17 @@ private static void CheckLayout(DeviceLayout layout, string hash, HashSet x.Value["UniqueName"] is not null)) { - // TODO: try catch try { - var layout = GetLayout(file); - var hash = layout.Hash; + var layout = GetLayout(name, token); + var hash = layout.Hash; if (layout is null) continue; @@ -222,4 +231,83 @@ public static List GetAllLayouts() .ToList(); } + const string refStr = "\"@ref\""; + private static Dictionary GetLayoutsWithPathReplaced(string directory) + { + StringBuilder sb = new(); + sb.Append("{"); + foreach (var path in Directory.GetFiles(directory, "*.json", SearchOption.AllDirectories)) + { + sb.Append( + $$""" + "{{Path.GetRelativePath(directory, path).Replace('\\', '/')}}" : {{File.ReadAllText(path)}}, + """); + } + + sb.Remove(sb.Length - 1, 1); + sb.Append("}"); + var allCombined = sb.ToString(); + var jo = JsonNode.Parse(allCombined, + documentOptions: + new System.Text.Json.JsonDocumentOptions() + { + CommentHandling = System.Text.Json.JsonCommentHandling.Skip, + AllowTrailingCommas = true + }); + int currentIndex = 0; + while (currentIndex < allCombined.Length) + { + var refIndex = allCombined.IndexOf(refStr, currentIndex); + if (refIndex == -1) + break; + int refFrom = 0; + int refTo = 0; + byte foundQuotations = 0; + for (int i = refIndex + refStr.Length; i < allCombined.Length - 1; i++) + { + if (allCombined[i] == '"' && allCombined[i - 1] != '\\') + { + foundQuotations++; + if (foundQuotations == 1) + { + refFrom = i; + } + else + { + refTo = i + 1; + break; + } + } + } + if (foundQuotations < 2) + break; + refIndex = allCombined.LastIndexOf('{', refIndex); + var removeTo = allCombined.IndexOf('}', refTo) + 1; + var path = allCombined[(refFrom + 1)..(refTo - 1)]; + allCombined = allCombined.Remove(refIndex, removeTo - refIndex); + + var jPath = JsonPath.Parse(path); + var res = jPath.Evaluate(jo); + if (res.Matches.Count == 0) + { + throw new JsonPathNotResolvableException(path); + } + var match = res.Matches[0]; + var key = jPath.Segments.Last().Selectors.Last().ToString().Replace("'", "\""); + allCombined = allCombined.Insert(refIndex, match.Value.ToJsonString()); + currentIndex = refIndex; + } + + return + JsonConvert + .DeserializeObject>(allCombined); + + } + private class JsonPathNotResolvableException : Exception + { + public JsonPathNotResolvableException(string? path) : base($"The json path ´{path}´ is not resolvable.") + { + + } + } } diff --git a/AppBroker.Core/IconService.cs b/AppBroker.Core/IconService.cs index b987fe4..826f74c 100644 --- a/AppBroker.Core/IconService.cs +++ b/AppBroker.Core/IconService.cs @@ -4,8 +4,6 @@ namespace AppBroker.Core; -[NonSucking.Framework.Serialization.Nooson] - public partial record SvgIcon(string Name, string Hash,[property: JsonIgnore] string Path, byte[]? Data, string TypeName =""); diff --git a/AppBroker.IOBroker/Plugin.cs b/AppBroker.IOBroker/Plugin.cs index abd2f5b..a067f10 100644 --- a/AppBroker.IOBroker/Plugin.cs +++ b/AppBroker.IOBroker/Plugin.cs @@ -12,7 +12,7 @@ namespace AppBroker.IOBroker; public class Plugin : IPlugin { public string Name { get; } - public int LoadOrder => int.MinValue; + public int LoadOrder => 0; public bool Initialize(LogFactory logFactory) { diff --git a/AppBroker.Mail/AppBroker.Mail.csproj b/AppBroker.Mail/AppBroker.Mail.csproj index 78bf2f7..384237c 100644 --- a/AppBroker.Mail/AppBroker.Mail.csproj +++ b/AppBroker.Mail/AppBroker.Mail.csproj @@ -1,15 +1,15 @@ - - net9.0 - enable - enable - true - + + net9.0 + enable + enable + true + - - - + + + diff --git a/AppBroker.Mail/Configuration/MailConfig.cs b/AppBroker.Mail/Configuration/MailConfig.cs new file mode 100644 index 0000000..ca35ecc --- /dev/null +++ b/AppBroker.Mail/Configuration/MailConfig.cs @@ -0,0 +1,15 @@ +using AppBroker.Core.Configuration; + + +namespace AppBroker.Windmill.Configuration; + +public class MailConfig : IConfig +{ + public string Name => "Mail"; + + public string Host { get; set; } + public ushort Port {get;set;} + public bool UseSsl {get;set;} + public string Username {get;set;} + public string Password {get;set;} +} diff --git a/AppBroker.Mail/Plugin.cs b/AppBroker.Mail/Plugin.cs index 161dd6f..4f9a100 100644 --- a/AppBroker.Mail/Plugin.cs +++ b/AppBroker.Mail/Plugin.cs @@ -1,14 +1,11 @@ using AppBroker.Core; using AppBroker.Core.Devices; using AppBroker.Core.Extension; +using AppBroker.Windmill.Configuration; using AppBroker.Zigbee2Mqtt.Devices; -using AppBrokerASP; - using MailKit; using MailKit.Net.Smtp; -using MailKit.Security; - using MimeKit; @@ -20,18 +17,19 @@ namespace AppBroker.Zigbee2Mqtt; internal class Plugin : IPlugin { - private Logger logger; - private Dictionary lastReceivedTimer = new Dictionary(); - public int LoadOrder => int.MinValue; + public int LoadOrder => 0; public string Name => "Mail"; + private Logger logger; + private Dictionary lastReceivedTimer = new Dictionary(); + private MailConfig config; public bool Initialize(LogFactory logFactory) { logger = logFactory.GetCurrentClassLogger(); IInstanceContainer.Instance.DeviceStateManager.StateChanged += DeviceStateManager_StateChanged; - + config ??= IInstanceContainer.Instance.ConfigManager.PluginConfigs.OfType().First(); return true; } @@ -70,8 +68,8 @@ private void SendMail(object? state) client.CheckCertificateRevocation = true; client.ServerCertificateValidationCallback = (_, __, ___, ____) => true; - await client.ConnectAsync("mail.gallimathias.de", 465, useSsl: true); - await client.AuthenticateAsync("smarthome@susch.eu", "ocj+V}#R0c=>_`,R4:Ud'U;(e&qOZytoz3$Um]jpFxfR{1CN=YIph0x~.+? AllOneTimeNotifications() .Select(x => x.Value) .ToListAsync()); } + + [HttpGet("allGlobalNotifications")] + public List AllGlobalNotifications() + { + return DeviceLayoutService + .GetAllLayouts() + .Where(x => x.NotificationSetup != null) + .SelectMany(x => x.NotificationSetup!) + .Where(x => x.Global) + .ToList(); + } } diff --git a/AppBroker.Notifications/Plugin.cs b/AppBroker.Notifications/Plugin.cs index 188b4a0..80965c4 100644 --- a/AppBroker.Notifications/Plugin.cs +++ b/AppBroker.Notifications/Plugin.cs @@ -23,7 +23,7 @@ internal class Plugin : IPlugin private Logger logger; public string Name => "Notifications"; - public int LoadOrder => int.MinValue; + public int LoadOrder => 0; public void RegisterTypes() { diff --git a/AppBroker.PainlessMesh/AppBroker.PainlessMesh.csproj b/AppBroker.PainlessMesh/AppBroker.PainlessMesh.csproj index a73e0db..33df680 100644 --- a/AppBroker.PainlessMesh/AppBroker.PainlessMesh.csproj +++ b/AppBroker.PainlessMesh/AppBroker.PainlessMesh.csproj @@ -27,7 +27,7 @@ - + diff --git a/AppBroker.PainlessMesh/BinarySmarthomeMessage.cs b/AppBroker.PainlessMesh/BinarySmarthomeMessage.cs index c46eb78..82c1777 100644 --- a/AppBroker.PainlessMesh/BinarySmarthomeMessage.cs +++ b/AppBroker.PainlessMesh/BinarySmarthomeMessage.cs @@ -3,22 +3,35 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using NonSucking.Framework.Serialization; + namespace AppBroker.PainlessMesh; + [NonSucking.Framework.Serialization.Nooson] public partial class BinarySmarthomeMessage : BaseSmarthomeMessage { public SmarthomeHeader Header { get; set; } - //public override uint NodeId { get => base.NodeId; set => base.NodeId = value; } + + [JsonIgnore] + public override long NodeId { get => base.NodeId; set => base.NodeId = value; } + + [JsonProperty("id")] + [NoosonIgnore] + public uint NodeIdInt => (uint)base.NodeId; [JsonProperty("m"), JsonConverter(typeof(StringEnumConverter))] public override MessageType MessageType { get => base.MessageType; set => base.MessageType = value; } [JsonConverter(typeof(StringEnumConverter)), JsonProperty("c")] public override Command Command { get => base.Command; set => base.Command = value; } - public ByteLengthList Parameters { get; set; } //public List Parameters2 { get; set; } + [NoosonPreferredCtor] + public BinarySmarthomeMessage(long nodeId, MessageType messageType, Command command, ByteLengthList parameters) : this((uint)nodeId, messageType, command, parameters) + { + } + public BinarySmarthomeMessage(uint nodeId, MessageType messageType, Command command, params byte[][] parameters) : this(nodeId, messageType, command, new ByteLengthList(parameters)) { } @@ -49,6 +62,7 @@ public BinarySmarthomeMessage() { } + } public partial struct SmarthomeHeader diff --git a/AppBroker.PainlessMesh/Plugin.cs b/AppBroker.PainlessMesh/Plugin.cs index 99d375c..b5c6498 100644 --- a/AppBroker.PainlessMesh/Plugin.cs +++ b/AppBroker.PainlessMesh/Plugin.cs @@ -15,7 +15,7 @@ namespace AppBroker.PainlessMesh; internal class Plugin : IPlugin { public string Name => "Plainless Mesh"; - public int LoadOrder => int.MinValue; + public int LoadOrder => 0; public bool Initialize(LogFactory logFactory) { diff --git a/AppBroker.PainlessMesh/SmarthomeMeshManager.cs b/AppBroker.PainlessMesh/SmarthomeMeshManager.cs index 2f794c4..44655d7 100644 --- a/AppBroker.PainlessMesh/SmarthomeMeshManager.cs +++ b/AppBroker.PainlessMesh/SmarthomeMeshManager.cs @@ -160,9 +160,9 @@ public void SocketClientDataReceived(BinarySmarthomeMessage e) { logger.Debug("Received a who am i response from " + e.NodeId); if (!knownNodeIds.Any(x => x.Id == e.NodeId)) - knownNodeIds.Add(new NodeSync(e.NodeId, 0)); + knownNodeIds.Add(new NodeSync(e.NodeIdInt, 0)); - _ = whoIAmSendTime.TryRemove(e.NodeId, out (DateTime time, int count) asda); + _ = whoIAmSendTime.TryRemove(e.NodeIdInt, out (DateTime time, int count) asda); if (e.Parameters != null) { NewConnectionEstablished?.Invoke(this, (e.NodeId, e.Parameters)); @@ -174,18 +174,18 @@ public void SocketClientDataReceived(BinarySmarthomeMessage e) NodeSync? known = knownNodeIds.FirstOrDefault(x => x.Id == e.NodeId); if (known == default) { - if (!whoIAmSendTime.TryGetValue(e.NodeId, out (DateTime time, int count) dt) || dt.time.Add(waitBeforeWhoIAmSendAgain) > DateTime.UtcNow) + if (!whoIAmSendTime.TryGetValue(e.NodeIdInt, out (DateTime time, int count) dt) || dt.time.Add(waitBeforeWhoIAmSendAgain) > DateTime.UtcNow) { //SendSingle(e.NodeId, new BinarySmarthomeMessage(0, MessageType.Get, Command.WhoIAm)); if (dt == default) - _ = whoIAmSendTime.TryAdd(e.NodeId, (DateTime.UtcNow.Subtract(waitBeforeWhoIAmSendAgain), 0)); + _ = whoIAmSendTime.TryAdd(e.NodeIdInt, (DateTime.UtcNow.Subtract(waitBeforeWhoIAmSendAgain), 0)); else - whoIAmSendTime[e.NodeId] = (DateTime.UtcNow.Subtract(waitBeforeWhoIAmSendAgain), 0); + whoIAmSendTime[e.NodeIdInt] = (DateTime.UtcNow.Subtract(waitBeforeWhoIAmSendAgain), 0); } - if (!queuedMessages.TryGetValue(e.NodeId, out Queue? queue)) + if (!queuedMessages.TryGetValue(e.NodeIdInt, out Queue? queue)) { queue = new Queue(); - queuedMessages.Add(e.NodeId, queue); + queuedMessages.Add(e.NodeIdInt, queue); } queue.Enqueue(e); return; @@ -197,11 +197,11 @@ public void SocketClientDataReceived(BinarySmarthomeMessage e) ConnectionReestablished?.Invoke(this, (known.Id, e.Parameters)); } - if (queuedMessages.TryGetValue(e.NodeId, out Queue? messages)) + if (queuedMessages.TryGetValue(e.NodeIdInt, out Queue? messages)) { while (messages.TryDequeue(out BinarySmarthomeMessage? message)) MessageTypeSwitch(message); - _ = queuedMessages.Remove(e.NodeId); + _ = queuedMessages.Remove(e.NodeIdInt); } MessageTypeSwitch(e); diff --git a/AppBroker.Runtime/AppBroker.Runtime.csproj b/AppBroker.Runtime/AppBroker.Runtime.csproj new file mode 100644 index 0000000..938a16c --- /dev/null +++ b/AppBroker.Runtime/AppBroker.Runtime.csproj @@ -0,0 +1,38 @@ + + + + net9.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppBroker.Runtime/AssemblyAttributes.cs b/AppBroker.Runtime/AssemblyAttributes.cs new file mode 100644 index 0000000..db45463 --- /dev/null +++ b/AppBroker.Runtime/AssemblyAttributes.cs @@ -0,0 +1,5 @@ + + +using AppBroker.Core.Extension; + +[assembly: Plugin()] \ No newline at end of file diff --git a/AppBroker.Runtime/Plugin.cs b/AppBroker.Runtime/Plugin.cs new file mode 100644 index 0000000..6efc5dd --- /dev/null +++ b/AppBroker.Runtime/Plugin.cs @@ -0,0 +1,383 @@ +using AppBroker.Core; +using AppBroker.Core.DynamicUI; +using AppBroker.Core.Extension; +using AppBroker.Runtime.Swagger; + +using AppBrokerASP; +using AppBrokerASP.Plugins; + +using Makaretu.Dns; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Any; + +using MQTTnet; +using MQTTnet.AspNetCore; +using MQTTnet.Server; + +using Newtonsoft.Json; + +using NLog; +using NLog.Extensions.Logging; +using NLog.Web; + +using System.Globalization; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Text; + +namespace AppBroker.Runtime; + +internal class Plugin : IPlugin, IRuntimePlugin +{ + public string Name => "Runtime"; + public int LoadOrder => -100; + + public void RegisterTypes() + { + } + + public bool Initialize(LogFactory logFactory) + { + return true; + } + +#if DEBUG + public const bool IsDebug = true; + private const int port = 5056; +#else + public const bool IsDebug = false; + private const int port = 5055; +#endif + public static ushort UsedPortForSignalR { get; private set; } + + private Logger? mainLogger; + private WebApplication? app; + + public void InitializeStartup(string[] args) + { + _ = new InstanceContainer(PluginLoader.Instance); + + _ = DeviceLayoutService.InstanceDeviceLayouts; + + mainLogger = LogManager + .Setup() + .LoadConfigurationFromSection(InstanceContainer.Instance.ConfigManager.Configuration) + .GetCurrentClassLogger(); + + try + { + + UsedPortForSignalR = port; + if (InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenPort == 0) + mainLogger.Info($"ListenPort is not configured in the appsettings serverconfig section and therefore default port {port} will be used, when no port was passed into listen url."); + else + UsedPortForSignalR = InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenPort; + + string[] listenUrls; + if (InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls.Any()) + { + listenUrls = new string[InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls.Count]; + for (int i = 0; i < InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls.Count; i++) + { + string? item = InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls[i]; + try + { + var builder = new UriBuilder(item) + { + //TODO only override default ports? How do we advertise multiple ports or will there be a main port, multiple advertisments or what is the plan? + //if ((builder.Scheme == "http" && builder.Port == 80) || (builder.Scheme == "https" && builder.Port == 443)) + //{ + Port = UsedPortForSignalR + }; + //} + listenUrls[i] = builder.ToString(); + } + catch (UriFormatException ex) + { + mainLogger.Error($"Error during process of {item}", ex); + } + } + } + else + { + listenUrls = new[] { $"http://*:{UsedPortForSignalR}" }; + } + AdvertiseServerPortsViaMDNS(UsedPortForSignalR); + + mainLogger.Info($"Listening on urls {string.Join(",", listenUrls)}"); + + WebApplicationBuilder? webBuilder = WebApplication + .CreateBuilder(new WebApplicationOptions() { Args = args, WebRootPath = "wwwroot" }); + + _ = webBuilder.Host.ConfigureAppConfiguration((hostingContext, config) => + { + config.Sources.Clear(); + _ = config.AddConfiguration(InstanceContainer.Instance.ConfigManager.Configuration); + }) + ; + + + _ = webBuilder.WebHost.UseKestrel((ks) => + { + foreach (var url in listenUrls) + { + var uri = new Uri(url); + ks.Listen(CreateIPEndPoint(uri.Host + ":" + uri.Port)); + } + + if (InstanceContainer.Instance.ConfigManager.MqttConfig.Enabled) + { + ks.ListenAnyIP(InstanceContainer.Instance.ConfigManager.MqttConfig.Port, x => x.UseMqtt()); + } + }); + + _ = webBuilder.Host.ConfigureLogging(logging => + { + _ = logging.ClearProviders(); + _ = logging.AddNLog(); + }); + var mqttConfig = InstanceContainer.Instance.ConfigManager.MqttConfig; + + var startup = new Startup(webBuilder.Configuration); + startup.ConfigureServices(webBuilder.Services); + List hubTypes = new(); + foreach (var extender in PluginLoader.Instance.ServiceExtenders) + { + extender.ConfigureServices(webBuilder.Services); + foreach (var type in extender.GetHubTypes()) + { + hubTypes.Add(type); + } + } + webBuilder.Services.AddSwaggerGen(s => + { + s.SchemaFilter(); + s.SupportNonNullableReferenceTypes(); + s.NonNullableReferenceTypesAsRequired(); + }); + webBuilder.Services.AddSwaggerGenNewtonsoftSupport(); + //webBuilder.Services.AddOpenApi("swagger", c => + //{ + // Type[] intEnums = new[] { typeof(Command) }; + // c.AddSchemaTransformer((d, c, cancel) => + // { + // if (c.JsonTypeInfo.Type.IsEnum) + // { + // if (intEnums.Contains(c.JsonTypeInfo.Type)) + // { + // d.Format = "integer"; + // } + // else + // { + // var array = new OpenApiArray(); + // array.AddRange(Enum.GetNames(c.JsonTypeInfo.Type).Select(n => new OpenApiString(n))); + // d.Extensions.Add("x-enumNames", array); + // d.Extensions.Add("x-enum-varnames", array); + // } + // } + // return Task.CompletedTask; + // }); + //}); + + app = webBuilder.Build(); + _ = app.UseWebSockets(); + _ = app.UseCors("CorsPolicy"); + _ = app.UseRouting(); + _ = app.UseStaticFiles(); + + Type dynamicHub = GenerateDynamicHub(hubTypes, mainLogger); + _ = app.UseEndpoints(e => + { + //_ = e.MapFallbackToPage("/_Host"); + _ = e.MapControllers(); + var mapHubMethod = typeof(HubEndpointRouteBuilderExtensions).GetMethod("MapHub", 1, new[] { typeof(IEndpointRouteBuilder), typeof(string) }); + _ = mapHubMethod.MakeGenericMethod(dynamicHub).Invoke(null, new object[] { e, "/Smarthome" }); + _ = mapHubMethod.MakeGenericMethod(dynamicHub).Invoke(null, new object[] { e, "/Smarthome/{id}" }); + + foreach (var extender in PluginLoader.Instance.ServiceExtenders) + { + extender.UseEndpoints(e); + } + + if (mqttConfig.Enabled) + { + _ = e.MapConnectionHandler( + "/MQTTClient", + httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector = + protocolList => protocolList.FirstOrDefault() ?? string.Empty); + } + }); + + if (mqttConfig.Enabled) + { + _ = app.UseMqttServer(server => + { + static async Task Server_RetainedMessagesClearedAsync(EventArgs arg) => File.Delete(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath); + static Task Server_LoadingRetainedMessageAsync(LoadingRetainedMessagesEventArgs arg) + { + if (File.Exists(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath)) + { + var json = File.ReadAllText(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath); + arg.LoadedRetainedMessages = JsonConvert.DeserializeObject>(json); + } + else + { + arg.LoadedRetainedMessages = new List(); + } + return Task.CompletedTask; + } + + static async Task Server_RetainedMessageChangedAsync(RetainedMessageChangedEventArgs arg) => File.WriteAllText(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath, JsonConvert.SerializeObject(arg.StoredRetainedMessages)); + + server.RetainedMessageChangedAsync += Server_RetainedMessageChangedAsync; + server.LoadingRetainedMessageAsync += Server_LoadingRetainedMessageAsync; + server.RetainedMessagesClearedAsync += Server_RetainedMessagesClearedAsync; + // Todo: Do something with the server + // https://github.com/dotnet/MQTTnet/wiki/Server#aspnet-50= + }); + } + if (InstanceContainer.Instance.ServerConfigManager.ServerConfig.EnableJavaScript) + { + InstanceContainer.Instance.JavaScriptEngineManager.Initialize(); + } + + //app.UseSwagger(); + //app.UseSwaggerUI(); + //app.MapOpenApi(); //Broken :( + + app.UseSwagger(); + app.UseReDoc(); // serve ReDoc UI + + } + catch (Exception ex) + { + mainLogger.Error(ex, "Error during initialize"); + throw; + } + } + + public void Run() + { + if (app is null || mainLogger is null) + return; + try + { + app.Run(); + } + catch (Exception ex) + { + + mainLogger.Error(ex, "Stopped program because of exception"); + throw; + } + finally + { + NLog.LogManager.Shutdown(); + } + } + + + private static IPEndPoint CreateIPEndPoint(string endPoint) + { + string[] ep = endPoint.Split(':'); + if (ep.Length < 2) + throw new FormatException("Invalid endpoint format"); + + IPAddress? ip; + if (ep.Length > 2) + { + if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip)) + throw new FormatException("Invalid ip-adress"); + } + else + { + if (!IPAddress.TryParse(ep[0], out ip)) + throw new FormatException("Invalid ip-adress"); + } + + if (!int.TryParse(ep[^1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out int port)) + throw new FormatException("Invalid port"); + + return new IPEndPoint(ip, port); + } + private static void AdvertiseServerPortsViaMDNS(ushort port) + { + MulticastService mdns = new(); + ServiceDiscovery sd = new(mdns); + + IPAddress[]? hostEntry + = Dns.GetHostEntry(Environment.MachineName) + .AddressList + .Where(x => x.AddressFamily == AddressFamily.InterNetwork + || (x.AddressFamily == AddressFamily.InterNetworkV6 + && !x.IsIPv6SiteLocal + && !x.IsIPv6LinkLocal + && !x.IsIPv6UniqueLocal + && !x.IsIPv6Teredo)) + .ToArray(); + + var serv = new ServiceProfile(InstanceContainer.Instance.ServerConfigManager.ServerConfig.InstanceName, "_smarthome._tcp", port, hostEntry); + + //serv.AddProperty("Min App Version", "0.0.2"); //Currently not needed, but supported by flutter app + if (IsDebug) + serv.AddProperty("Debug", IsDebug.ToString()); + if (string.IsNullOrWhiteSpace(InstanceContainer.Instance.ServerConfigManager.ServerConfig.ClusterId)) + serv.AddProperty("ClusterId", InstanceContainer.Instance.ServerConfigManager.ServerConfig.ClusterId); + sd.Advertise(serv); + + mdns.Start(); + } + + private static Type GenerateDynamicHub(List hubTypes, Logger mainLogger) + { + AssemblyName assemblyName = new AssemblyName("DynamicHubAssembly"); + AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule"); + TypeBuilder typeBuilder = moduleBuilder.DefineType("RuntimeHub", TypeAttributes.Public, typeof(DynamicHub)); + foreach (var hubType in hubTypes) + { + foreach (var method in hubType.GetMethods()) + { + if (method.DeclaringType != hubType || (method.Attributes & MethodAttributes.Private) > 0) + continue; + if ((method.Attributes & MethodAttributes.Static) == 0) + { + mainLogger.Warn("Method {0}.{1} was not static, only public static methods are supported", hubType.FullName, method.Name); + continue; + } + var parameters = method.GetParameters(); + bool passThis = false; + if (parameters.Length > 0 && parameters[0].ParameterType == typeof(DynamicHub)) + passThis = true; + MethodBuilder methodBuilder = typeBuilder.DefineMethod( + method.Name, + MethodAttributes.Public | MethodAttributes.HideBySig, + method.ReturnType, + parameters.Skip(passThis ? 1 : 0).Select(x => x.ParameterType).ToArray()); + + var gen = methodBuilder.GetILGenerator(); + + for (int i = 0; i < parameters.Length; i++) + { + gen.Emit(OpCodes.Ldarg, i + (passThis ? 0 : 1)); + } + gen.EmitCall(OpCodes.Call, method, null); + gen.Emit(OpCodes.Ret); + } + } + Type dynamicType = typeBuilder.CreateType(); + return dynamicType; + } + +} diff --git a/AppBrokerASP/Startup.cs b/AppBroker.Runtime/Startup.cs similarity index 74% rename from AppBrokerASP/Startup.cs rename to AppBroker.Runtime/Startup.cs index f5599be..a05fdf1 100644 --- a/AppBrokerASP/Startup.cs +++ b/AppBroker.Runtime/Startup.cs @@ -11,8 +11,13 @@ using Newtonsoft.Json.Linq; using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.OpenApi; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using AppBrokerASP; -namespace AppBrokerASP; +namespace AppBroker.Runtime; public class Startup { @@ -29,25 +34,6 @@ public void ConfigureServices(IServiceCollection services) options.MinimumSameSitePolicy = SameSiteMode.None); - //services.AddEndpointsApiExplorer(); - //services.AddSwaggerGen((c) => - //{ - // c.MapType(() => new OpenApiSchema() - // { - // OneOf = [ - // new OpenApiSchema() { Type = "object" }, - // new OpenApiSchema() { Type = "number" }, - // new OpenApiSchema() { Type = "integer" }, - // new OpenApiSchema() { Type = "boolean" }, - // new OpenApiSchema() { Type = "array" }, - // new OpenApiSchema() { Type = "string" }, - // ], - // Nullable = true - // }); - // c.SupportNonNullableReferenceTypes(); - // c.UseAllOfToExtendReferenceSchemas(); - // //opt.MapType(() => new OpenApiSchema { Type = typeof(JToken).Name }); - //}); _ = services.AddCors(options => options.AddPolicy("CorsPolicy", builder => _ = builder .AllowAnyMethod() @@ -77,9 +63,6 @@ public void ConfigureServices(IServiceCollection services) signalRBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - //_ = services.AddRazorPages(); - //services.AddOpenApi("appbroker"); - //services.AddOpenApiDocument(); services.AddSwaggerDocument(c => { c.RequireParametersWithoutDefault = true; @@ -88,7 +71,7 @@ public void ConfigureServices(IServiceCollection services) _ = services.AddSingleton(); var container = InstanceContainer.Instance; - _ = services.AddSingleton(new CloudConnector()); + //_ = services.AddSingleton(new CloudConnector()); _ = services.AddSingleton(container); _ = services.AddSingleton(container.IconService); _ = services.AddSingleton(container.ConfigManager); @@ -99,8 +82,6 @@ public void ConfigureServices(IServiceCollection services) _ = services.AddSingleton(container.DeviceStateManager); _ = services.AddSingleton(container.HistoryManager); - - if (InstanceContainer.Instance.ConfigManager.MqttConfig.Enabled) { _ = services diff --git a/AppBroker.Runtime/Swagger/EnumSchemaFilter.cs b/AppBroker.Runtime/Swagger/EnumSchemaFilter.cs new file mode 100644 index 0000000..0360a8b --- /dev/null +++ b/AppBroker.Runtime/Swagger/EnumSchemaFilter.cs @@ -0,0 +1,38 @@ +using AppBroker.Core; + +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; + +using Swashbuckle.AspNetCore.SwaggerGen; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AppBroker.Runtime.Swagger; +internal class EnumSchemaFilter : ISchemaFilter +{ + private Type[] intEnums = [typeof(Command)]; + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + if (intEnums.Contains(context.Type)) + { + schema.Enum.Clear(); + schema.Type = "integer"; + + var enumUnderlyingType = Enum.GetUnderlyingType(context.Type); + schema.Format = enumUnderlyingType.Name.ToLower(); + } + else if (context.Type.IsEnum) + { + var array = new OpenApiArray(); + array.AddRange(Enum.GetNames(context.Type).Select(n => new OpenApiString(n))); + // NSwag + schema.Extensions.Add("x-enumNames", array); + // Openapi-generator + schema.Extensions.Add("x-enum-varnames", array); + } + } +} diff --git a/AppBroker.Windmill/Controller/WindmillController.cs b/AppBroker.Windmill/Controller/WindmillController.cs new file mode 100644 index 0000000..dd74f86 --- /dev/null +++ b/AppBroker.Windmill/Controller/WindmillController.cs @@ -0,0 +1,63 @@ +using AppBroker.Core.Devices; +using AppBroker.Core; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SignalR; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AppBroker.Core.Managers; +using AppBrokerASP; +using System.Text.Json; +using NLog; +using AppBroker.Windmill.Model; + +namespace AppBroker.Windmill.Controller; + + +/// +/// Specific Controller for Windmill Workflow Engine +/// +[Route("windmill")] +public class WindmillController : ControllerBase +{ + private readonly IDeviceManager deviceManager; + + public WindmillController(IDeviceManager deviceManager) + { + this.deviceManager = deviceManager; + } + + /// + /// Used to update things on the app + /// + /// + /// + [HttpPost] + public async Task Update([FromBody] WindmillSmarthomeMessage message) + { + message.NodeId = long.Parse(message.NodeIdHex, System.Globalization.NumberStyles.HexNumber); + + if (deviceManager.Devices.TryGetValue(message.NodeId, out Device? device)) + { + switch (message.MessageType) + { + case MessageType.Get: + break; + case MessageType.Update: + await device.UpdateFromApp(message.Command, message.Parameters); + break; + case MessageType.Options: + device.OptionsFromApp(message.Command, message.Parameters); + break; + default: + break; + } + } + } + + +} diff --git a/AppBroker.Windmill/Model/WindmillSmarthomeMessage.cs b/AppBroker.Windmill/Model/WindmillSmarthomeMessage.cs new file mode 100644 index 0000000..2afbd2c --- /dev/null +++ b/AppBroker.Windmill/Model/WindmillSmarthomeMessage.cs @@ -0,0 +1,24 @@ +using AppBroker.Core; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AppBroker.Windmill.Model +{ + public class WindmillSmarthomeMessage : BaseSmarthomeMessage + { + public List Parameters { get; set; } + + [JsonProperty("id")] + public override long NodeId { get; set; } + + [JsonProperty("idHex")] + public string NodeIdHex { get; set; } = ""; + } +} diff --git a/AppBroker.Windmill/Plugin.cs b/AppBroker.Windmill/Plugin.cs index 87b0afa..3b264e5 100644 --- a/AppBroker.Windmill/Plugin.cs +++ b/AppBroker.Windmill/Plugin.cs @@ -12,7 +12,7 @@ namespace AppBroker.Windmill; internal class Plugin : IPlugin { public string Name => "Windmill"; - public int LoadOrder => int.MinValue; + public int LoadOrder => 0; public void RegisterTypes() { diff --git a/AppBroker.Zigbee2Mqtt/Devices/Zigbee2MqttDevice.cs b/AppBroker.Zigbee2Mqtt/Devices/Zigbee2MqttDevice.cs index 21184ec..81ecc1b 100644 --- a/AppBroker.Zigbee2Mqtt/Devices/Zigbee2MqttDevice.cs +++ b/AppBroker.Zigbee2Mqtt/Devices/Zigbee2MqttDevice.cs @@ -184,6 +184,7 @@ private void InvokeOnDevice(string property, Action parameters) { + logger.Debug($"Got an update message {command} with paramameters [{string.Join(',', parameters.Select(x => x.ToString()))}]"); switch (command) { case Command.Zigbee: @@ -234,7 +235,8 @@ protected override Context ExtendEngine(Context engine) protected override bool FriendlyNameChanging(string oldName, string newName) { - + if (oldName == newName) + return false; if (string.IsNullOrWhiteSpace(newName)) return false; try @@ -245,9 +247,8 @@ protected override bool FriendlyNameChanging(string oldName, string newName) return true; } logger.Info($"Trying to rename {oldName} to {newName} for device with id {Id}"); -#if !(DEBUG) - client.EnqueueAsync("zigbee2mqtt/bridge/request/device/rename", $"{{\"from\": \"{oldName}\", \"to\": \"{newName}\"}}"); -#endif + zigbeeManager.RenameDevice(oldName, newName); + if (zigbeeManager.friendlyNameToIdMapping.TryGetValue(oldName, out var id) && id == Id && !zigbeeManager.friendlyNameToIdMapping.ContainsKey(newName) diff --git a/AppBroker.Zigbee2Mqtt/Plugin.cs b/AppBroker.Zigbee2Mqtt/Plugin.cs index c116567..9147723 100644 --- a/AppBroker.Zigbee2Mqtt/Plugin.cs +++ b/AppBroker.Zigbee2Mqtt/Plugin.cs @@ -12,7 +12,7 @@ namespace AppBroker.Zigbee2Mqtt; internal class Plugin : IPlugin { public string Name => "Zigbee2MQTT"; - public int LoadOrder => int.MinValue; + public int LoadOrder => 0; public void RegisterTypes() { diff --git a/AppBroker.Zigbee2Mqtt/Zigbee2MqttConfig.cs b/AppBroker.Zigbee2Mqtt/Zigbee2MqttConfig.cs index a5505ab..6704b67 100644 --- a/AppBroker.Zigbee2Mqtt/Zigbee2MqttConfig.cs +++ b/AppBroker.Zigbee2Mqtt/Zigbee2MqttConfig.cs @@ -8,6 +8,7 @@ public class Zigbee2MqttConfig public string Address { get; set; } public int Port { get; set; } public bool RestartOnMissingDevice{ get; set; } + public bool SyncNamesToZigbee2Mqtt { get; set; } public Zigbee2MqttConfig() { @@ -16,5 +17,6 @@ public Zigbee2MqttConfig() Address = "127.0.0.1"; Port = 8999; RestartOnMissingDevice = false; + SyncNamesToZigbee2Mqtt = false; } } diff --git a/AppBroker.Zigbee2Mqtt/Zigbee2MqttManager.cs b/AppBroker.Zigbee2Mqtt/Zigbee2MqttManager.cs index 7237e00..2174232 100644 --- a/AppBroker.Zigbee2Mqtt/Zigbee2MqttManager.cs +++ b/AppBroker.Zigbee2Mqtt/Zigbee2MqttManager.cs @@ -174,7 +174,7 @@ private async Task Mqtt_ApplicationMessageReceivedAsync(MqttApplicationMessageRe { var c = JsonConvert.DeserializeObject(payload); var manager = IInstanceContainer.Instance.DeviceStateManager; - if (!long.TryParse(c.Coordinator.IEEEAddress, NumberStyles.HexNumber, null, out var coordinatorId)) + if (!long.TryParse(c.Coordinator.IEEEAddress, NumberStyles.HexNumber, null, out var coordinatorId)) return; manager.SetSingleState(coordinatorId, nameof(c.PermitJoin), c.PermitJoin); manager.SetSingleState(coordinatorId, nameof(c.PermitJoinTimeout), c.PermitJoinTimeout); @@ -217,9 +217,13 @@ private async Task Mqtt_ApplicationMessageReceivedAsync(MqttApplicationMessageRe var dbDevice = ctx.Devices.FirstOrDefault(x => x.Id == id); if (dbDevice is not null && !string.IsNullOrWhiteSpace(dbDevice.FriendlyName) && !string.Equals(dbDevice.FriendlyName, item.FriendlyName, StringComparison.OrdinalIgnoreCase)) { - logger.Info($"Friendly name of Zigbee2Mqtt Device {item.FriendlyName} does not match saved name {dbDevice.FriendlyName}, updating"); - await MQTTClient.EnqueueAsync($"{config.Topic}/bridge/request/device/rename", $"{{\"from\": \"{item.IEEEAddress}\", \"to\": \"{dbDevice.FriendlyName}\"}}"); - item.FriendlyName = dbDevice.FriendlyName; + if (config.SyncNamesToZigbee2Mqtt) + { + logger.Info($"Friendly name of Zigbee2Mqtt Device {item.FriendlyName} does not match saved name {dbDevice.FriendlyName}, updating"); + RenameDevice(item.IEEEAddress, dbDevice.FriendlyName); + await MQTTClient.EnqueueAsync($"{config.Topic}/bridge/request/device/rename", $"{{\"from\": \"{item.IEEEAddress}\", \"to\": \"{dbDevice.FriendlyName}\"}}"); + item.FriendlyName = dbDevice.FriendlyName; + } } friendlyNameToIdMapping[item.FriendlyName] = id; @@ -303,19 +307,34 @@ private async Task Mqtt_ApplicationMessageReceivedAsync(MqttApplicationMessageRe } } + public async Task RenameDevice(string oldName, string friendlyName) + { + if (config.SyncNamesToZigbee2Mqtt) + { + await MQTTClient.EnqueueAsync($"{config.Topic}/bridge/request/device/rename", $"{{\"from\": \"{oldName}\", \"to\": \"{friendlyName}\"}}"); + } + } + private void TryInterpretTopicAsStateUpdate(string deviceName, string payload) { - if (friendlyNameToIdMapping.TryGetValue(deviceName, out var id)) + if (payload is not null && friendlyNameToIdMapping.TryGetValue(deviceName, out var id)) { - InstanceContainer - .Instance - .DeviceStateManager - .SetMultipleStates(id, ReplaceCustomStates(id, JsonConvert.DeserializeObject>(payload)!)); - - InstanceContainer - .Instance - .DeviceStateManager - .SetSingleState(id, "lastReceived", DateTime.UtcNow); + try + { + InstanceContainer + .Instance + .DeviceStateManager + .SetMultipleStates(id, ReplaceCustomStates(id, JsonConvert.DeserializeObject>(payload)!)); + + InstanceContainer + .Instance + .DeviceStateManager + .SetSingleState(id, "lastReceived", DateTime.UtcNow); + } + catch (Exception ex) + { + logger.Error(ex, "State could not be interpreted"); + } } } diff --git a/AppBroker.sln b/AppBroker.sln index d32d46c..0e773f0 100644 --- a/AppBroker.sln +++ b/AppBroker.sln @@ -50,6 +50,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppBroker.App", "AppBroker. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppBroker.Windmill", "AppBroker.Windmill\AppBroker.Windmill.csproj", "{D92403E4-F9D5-4AE6-844E-A2095521D811}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppBroker.Runtime", "AppBroker.Runtime\AppBroker.Runtime.csproj", "{20B6EC8F-FB33-496F-9385-FBD730B7BEDA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_Ubuntu|Any CPU = Debug_Ubuntu|Any CPU @@ -194,6 +196,14 @@ Global {D92403E4-F9D5-4AE6-844E-A2095521D811}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {D92403E4-F9D5-4AE6-844E-A2095521D811}.Release|Any CPU.ActiveCfg = Release|Any CPU {D92403E4-F9D5-4AE6-844E-A2095521D811}.Release|Any CPU.Build.0 = Release|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Release_Ubuntu|Any CPU.ActiveCfg = Release|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -216,6 +226,7 @@ Global {9D79D38F-DBB4-4768-8EEB-4DCBA7DD76B9} = {C3D29452-2BD2-45C6-A888-A7E2B27BC713} {39F713BA-3788-46D3-B88F-0ECD0E4C801A} = {C3D29452-2BD2-45C6-A888-A7E2B27BC713} {D92403E4-F9D5-4AE6-844E-A2095521D811} = {C3D29452-2BD2-45C6-A888-A7E2B27BC713} + {20B6EC8F-FB33-496F-9385-FBD730B7BEDA} = {C3D29452-2BD2-45C6-A888-A7E2B27BC713} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1C5A671A-8A0F-4853-AE0F-B01527987D90} diff --git a/AppBrokerASP/AppBrokerASP.csproj b/AppBrokerASP/AppBrokerASP.csproj index 35f7f7a..151ff9d 100644 --- a/AppBrokerASP/AppBrokerASP.csproj +++ b/AppBrokerASP/AppBrokerASP.csproj @@ -6,7 +6,7 @@ preview enable - + true CS0657;CA2252 true @@ -15,10 +15,10 @@ - - - - + + + + @@ -88,6 +88,10 @@ + + + + Always @@ -107,9 +111,9 @@ - - PreserveNewest - + + PreserveNewest + \ No newline at end of file diff --git a/AppBrokerASP/Cloud/CloudConnector.cs b/AppBrokerASP/Cloud/CloudConnector.cs index 6e83683..9fe106e 100644 --- a/AppBrokerASP/Cloud/CloudConnector.cs +++ b/AppBrokerASP/Cloud/CloudConnector.cs @@ -1,186 +1,188 @@ -using AppBroker.Core; - -using AppBrokerASP.Configuration; - -using NLog; - -using System.Net.Security; -using System.Net.Sockets; -using System.Text; - -namespace AppBrokerASP.Cloud; - -public class CloudConnector -{ - ConcurrentDictionary tcpClients = new(); - private readonly Logger mainLogger; - private readonly CloudConfig config; - - - public CloudConnector() - { - mainLogger = LogManager.GetCurrentClassLogger(); - config = InstanceContainer.Instance.ServerConfigManager.CloudConfig; - if (config.Enabled) - { - try - { - var client = new TcpClient(); - mainLogger.Warn("Waiting for TCP Connection"); - client.BeginConnect(config.CloudServerHost, config.CloudServerPort, OnClientConnect, client); - } - catch (Exception ex) - { - mainLogger.Error("No connection to the cloud server could be established", ex); - } - } - } - - void OnClientConnect(IAsyncResult x) - { - var client = (TcpClient)x.AsyncState!; - try - { - client.EndConnect(x); - mainLogger.Warn("Busy Waiting for new Connection >>>>>>>>>>>>>"); - AcceptNewClient(client); - } - catch (Exception) - { - try - { - client.BeginConnect(config.CloudServerHost, config.CloudServerPort, OnClientConnect, client); - - } - catch (Exception ex) - { - mainLogger.Error("No connection to the cloud server could be established", ex); - } - return; - } - client = new TcpClient(); - mainLogger.Warn("Waiting for TCP Connection >>>>>>>>>>>>> "); - client.BeginConnect(config.CloudServerHost, config.CloudServerPort, OnClientConnect, client); - } - - private void AcceptNewClient(TcpClient x) - { - mainLogger.Warn("Starting new connection >>>>>>>>>>>>> "); - var incomming = x; - - Stream incommingStr = incomming.GetStream(); - //var str = """ - //GET /SmartHome/YourUniqueConnectionId/Server undefined - //Host: smarthome.susch.eu - //User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0 - //Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 - //Accept-Language: de,en-US;q=0.7,en;q=0.3 - //Accept-Encoding: gzip, deflate, br - //Connection: keep-alive - //Upgrade-Insecure-Requests: 1 - //Sec-Fetch-Dest: document - //Sec-Fetch-Mode: navigate - //Sec-Fetch-Site: none - //Sec-Fetch-User: ?1 - //"""; - - //incommingStr.Write(Encoding.UTF8.GetBytes(str)); - //Thread.Sleep(250); - //Span msg = stackalloc byte[incomming.Available]; - //incommingStr.Read(msg); - //var msgStr = Encoding.UTF8.GetString(msg); - //Console.Write(msgStr); - - if (config.UseSSL) - { - - incommingStr = new SslStream(incommingStr); - var ssl = incommingStr as SslStream; - ssl.AuthenticateAsClient(config.CloudServerHost); - - } - /* -GET / HTTP/1.1 -Host: smarthome.susch.eu -Connection: keep-alive - */ - - //incommingStr.Write(Encoding.UTF8.GetBytes($"GET /SmartHome/{config.ConnectionID}/Server HTTP/1.1\r\nHost: smarthome.susch.eu\r\nConnection: keep-alive\r\n")); - incommingStr.Write(Encoding.UTF8.GetBytes($"/SmartHome/{config.ConnectionID}/Server")); - //Thread.Sleep(250); - //Span msg = stackalloc byte[incomming.Available]; - //incommingStr.Read(msg); - //var msgStr = Encoding.UTF8.GetString(msg); - //Console.Write(msgStr); - - Span firstMessage = stackalloc byte[1]; - - do - { - incommingStr.ReadExactly(firstMessage); - } while (firstMessage[0] != 1); - - - var self = new TcpClient(config.LocalHostName, Program.UsedPortForSignalR); - self.NoDelay = true; - var selfStr = self.GetStream(); - tcpClients[incomming] = self; - - _ = Task.Run(async () => - { - mainLogger.Warn("Server started >>>>>>>>>>>>> "); - while (true) - { - try - { - if (self.Available > 0) - { - var bytes = new byte[self.Available]; - selfStr.ReadExactly(bytes); - mainLogger.Warn($"Send back {bytes.Length}"); - var bp = System.Text.Encoding.UTF8.GetString(bytes); - incommingStr.Write(bytes); - } - } - catch (Exception ex) - { - mainLogger.Warn($"Send error (Client:{incomming.Connected}, Self:{self.Connected}) {ex}"); - tcpClients.Remove(incomming, out _); - incomming?.Close(); - self?.Close(); - break; - - } - await Task.Delay(1); - } - }); - _ = Task.Run(async () => - { - while (true) - { - try - { - if (incomming.Available > 0) - { - var bytes = new byte[incomming.Available]; - incommingStr.ReadExactly(bytes); - var bp = System.Text.Encoding.UTF8.GetString(bytes); - mainLogger.Warn($"Rec {bytes.Length}"); - selfStr.Write(bytes); - - } - } - catch (Exception ex) - { - mainLogger.Warn($"Rec error (Client:{incomming.Connected}, Self:{self.Connected}) {ex}"); - - tcpClients.Remove(incomming, out _); - incomming?.Close(); - self?.Close(); - break; - } - await Task.Delay(1); - } - }); - } -} +//TODO Has to be adjusted to http requests + signal R + +//using AppBroker.Core; + +//using AppBrokerASP.Configuration; + +//using NLog; + +//using System.Net.Security; +//using System.Net.Sockets; +//using System.Text; + +//namespace AppBrokerASP.Cloud; + +//public class CloudConnector +//{ +// ConcurrentDictionary tcpClients = new(); +// private readonly Logger mainLogger; +// private readonly CloudConfig config; + + +// public CloudConnector() +// { +// mainLogger = LogManager.GetCurrentClassLogger(); +// config = InstanceContainer.Instance.ServerConfigManager.CloudConfig; +// if (config.Enabled) +// { +// try +// { +// var client = new TcpClient(); +// mainLogger.Warn("Waiting for TCP Connection"); +// client.BeginConnect(config.CloudServerHost, config.CloudServerPort, OnClientConnect, client); +// } +// catch (Exception ex) +// { +// mainLogger.Error("No connection to the cloud server could be established", ex); +// } +// } +// } + +// void OnClientConnect(IAsyncResult x) +// { +// var client = (TcpClient)x.AsyncState!; +// try +// { +// client.EndConnect(x); +// mainLogger.Warn("Busy Waiting for new Connection >>>>>>>>>>>>>"); +// AcceptNewClient(client); +// } +// catch (Exception) +// { +// try +// { +// client.BeginConnect(config.CloudServerHost, config.CloudServerPort, OnClientConnect, client); + +// } +// catch (Exception ex) +// { +// mainLogger.Error("No connection to the cloud server could be established", ex); +// } +// return; +// } +// client = new TcpClient(); +// mainLogger.Warn("Waiting for TCP Connection >>>>>>>>>>>>> "); +// client.BeginConnect(config.CloudServerHost, config.CloudServerPort, OnClientConnect, client); +// } + +// private void AcceptNewClient(TcpClient x) +// { +// mainLogger.Warn("Starting new connection >>>>>>>>>>>>> "); +// var incomming = x; + +// Stream incommingStr = incomming.GetStream(); +// //var str = """ +// //GET /SmartHome/YourUniqueConnectionId/Server undefined +// //Host: smarthome.susch.eu +// //User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0 +// //Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 +// //Accept-Language: de,en-US;q=0.7,en;q=0.3 +// //Accept-Encoding: gzip, deflate, br +// //Connection: keep-alive +// //Upgrade-Insecure-Requests: 1 +// //Sec-Fetch-Dest: document +// //Sec-Fetch-Mode: navigate +// //Sec-Fetch-Site: none +// //Sec-Fetch-User: ?1 +// //"""; + +// //incommingStr.Write(Encoding.UTF8.GetBytes(str)); +// //Thread.Sleep(250); +// //Span msg = stackalloc byte[incomming.Available]; +// //incommingStr.Read(msg); +// //var msgStr = Encoding.UTF8.GetString(msg); +// //Console.Write(msgStr); + +// if (config.UseSSL) +// { + +// incommingStr = new SslStream(incommingStr); +// var ssl = incommingStr as SslStream; +// ssl.AuthenticateAsClient(config.CloudServerHost); + +// } +// /* +//GET / HTTP/1.1 +//Host: smarthome.susch.eu +//Connection: keep-alive +// */ + +// //incommingStr.Write(Encoding.UTF8.GetBytes($"GET /SmartHome/{config.ConnectionID}/Server HTTP/1.1\r\nHost: smarthome.susch.eu\r\nConnection: keep-alive\r\n")); +// incommingStr.Write(Encoding.UTF8.GetBytes($"/SmartHome/{config.ConnectionID}/Server")); +// //Thread.Sleep(250); +// //Span msg = stackalloc byte[incomming.Available]; +// //incommingStr.Read(msg); +// //var msgStr = Encoding.UTF8.GetString(msg); +// //Console.Write(msgStr); + +// Span firstMessage = stackalloc byte[1]; + +// do +// { +// incommingStr.ReadExactly(firstMessage); +// } while (firstMessage[0] != 1); + + +// var self = new TcpClient(config.LocalHostName, Program.UsedPortForSignalR); +// self.NoDelay = true; +// var selfStr = self.GetStream(); +// tcpClients[incomming] = self; + +// _ = Task.Run(async () => +// { +// mainLogger.Warn("Server started >>>>>>>>>>>>> "); +// while (true) +// { +// try +// { +// if (self.Available > 0) +// { +// var bytes = new byte[self.Available]; +// selfStr.ReadExactly(bytes); +// mainLogger.Warn($"Send back {bytes.Length}"); +// var bp = System.Text.Encoding.UTF8.GetString(bytes); +// incommingStr.Write(bytes); +// } +// } +// catch (Exception ex) +// { +// mainLogger.Warn($"Send error (Client:{incomming.Connected}, Self:{self.Connected}) {ex}"); +// tcpClients.Remove(incomming, out _); +// incomming?.Close(); +// self?.Close(); +// break; + +// } +// await Task.Delay(1); +// } +// }); +// _ = Task.Run(async () => +// { +// while (true) +// { +// try +// { +// if (incomming.Available > 0) +// { +// var bytes = new byte[incomming.Available]; +// incommingStr.ReadExactly(bytes); +// var bp = System.Text.Encoding.UTF8.GetString(bytes); +// mainLogger.Warn($"Rec {bytes.Length}"); +// selfStr.Write(bytes); + +// } +// } +// catch (Exception ex) +// { +// mainLogger.Warn($"Rec error (Client:{incomming.Connected}, Self:{self.Connected}) {ex}"); + +// tcpClients.Remove(incomming, out _); +// incomming?.Close(); +// self?.Close(); +// break; +// } +// await Task.Delay(1); +// } +// }); +// } +//} diff --git a/AppBrokerASP/DeviceLayouts/Base.json b/AppBrokerASP/DeviceLayouts/Base.json new file mode 100644 index 0000000..db61a94 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/Base.json @@ -0,0 +1,388 @@ +{ + "dashboard": { + "id": { + "Name": "id", + "Order": 4, + "RowNr": 400, + "ShowOnlyInDeveloperMode": true, + "Hex": true + }, + "contact": { + "Name": "contact", + "Order": 2, + "RowNr": 10, + "EditInfo": { + "EditType": "toggle", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "zigbee", + "Value": true, + "Parameters": [] + }, + { + "Command": "zigbee", + "Value": false, + "Parameters": [] + } + ], + "Display": "Status", + "ActiveValue": false + } + }, + "battery": { + "Name": "battery", + "Order": 0, + "EditInfo": { + "EditType": "icon", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "None", + "Value": 0, + "Min": 0, + "Max": 20, + "CodePoint": 59412, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 0, + "Min": 20, + "Max": 40, + "CodePoint": 59408, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 0, + "Min": 40, + "Max": 60, + "CodePoint": 59409, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 0, + "Min": 60, + "Max": 80, + "CodePoint": 59410, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 100, + "Min": 80, + "Max": 101, + "CodePoint": 59411, + "FontFamily": "Smarthome" + } + ] + }, + "SpecialType": "right" + }, + "temperature": { + "Name": "temperature", + "Order": 0, + "TextStyle": { + "FontSize": 25.0, + "FontWeight": "bold" + }, + "RowNr": 0, + "UnitOfMeasurement": " °C" + }, + "humidity": { + "Name": "humidity", + "Order": 1, + "RowNr": 10, + "TextStyle": { + "FontSize": 14.0, + "FontWeight": "normal" + }, + "UnitOfMeasurement": " %" + }, + "pressure": { + "Name": "pressure", + "Order": 2, + "RowNr": 10, + "TextStyle": { + "FontSize": 14.0, + "FontWeight": "normal" + }, + "UnitOfMeasurement": " kpa" + }, + "occupancy": { + "Name": "occupancy", + "Order": 2, + "RowNr": 10, + "EditInfo": { + "EditType": "toggle", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "zigbee", + "Value": true, + "Parameters": [] + }, + { + "Command": "zigbee", + "Value": false, + "Parameters": [] + } + ], + "Display": "Status", + "ActiveValue": true + } + }, + "load_power": { + "Name": "loadPower", + "Order": 0, + "RowNr": 10, + "TextStyle": { + "FontSize": 20.0, + "FontWeight": "bold" + }, + "UnitOfMeasurement": " W", + "Precision": 0 + }, + "current": { + "Name": "current", + "Order": 1, + "RowNr": 30, + "TextStyle": { + "FontSize": 16.0, + "FontWeight": "normal" + }, + "UnitOfMeasurement": " A", + "Precision": 2 + }, + "voltage": { + "Name": "voltage", + "Order": 2, + "RowNr": 30, + "TextStyle": { + "FontSize": 16.0, + "FontWeight": "normal" + }, + "UnitOfMeasurement": " V", + "Precision": 0 + } + }, + "detail": { + "friendly_name": { + "Name": "friendlyName", + "RowNr": 0, + "TextStyle": { + "FontSize": 25.0, + "FontFamily": "FontName", + "FontWeight": "bold", + "FontStyle": "normal" + }, + "SpecialType": "none" + }, + "id": { + "Name": "id", + "RowNr": 10, + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "bold", + "FontStyle": "normal" + }, + "SpecialType": "none", + "ShowOnlyInDeveloperMode": true, + "Hex": true + }, + "last_received": { + "Name": "lastReceivedFormatted", + "Order": 0, + "RowNr": 650, + "DisplayName": "Zuletzt empfangen: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none", + "Format": "dd" + }, + "link_quality": { + "Name": "linkQuality", + "Order": 0, + "RowNr": 700, + "DisplayName": "Verbindungsqualität: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + "temperature": { + "Name": "temperature", + "Order": 0, + "RowNr": 20, + "DisplayName": "Temperature: ", + "UnitOfMeasurement": " °C", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + "humidity": { + "Name": "humidity", + "Order": 0, + "RowNr": 30, + "DisplayName": "Luftfeuchtigkeit: ", + "UnitOfMeasurement": " %", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + "pressure": { + "Name": "pressure", + "Order": 0, + "RowNr": 40, + "DisplayName": "Luftdruck: ", + "UnitOfMeasurement": " kPA", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + "battery": { + "Name": "battery", + "Order": 0, + "RowNr": 50, + "DisplayName": "Battery: ", + "UnitOfMeasurement": " %", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + "milli_voltage": { + "Name": "voltage", + "Order": 0, + "RowNr": 60, + "DisplayName": "Spannung: ", + "UnitOfMeasurement": " mV", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + "contact": { + "Name": "contact", + "Order": 0, + "RowNr": 20, + "DisplayName": "Geoeffnet: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + "load_power": { + "Name": "loadPower", + "Order": 0, + "RowNr": 20, + "DisplayName": "Verbrauch: ", + "UnitOfMeasurement": " W", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none", + "Precision": 0 + }, + "current": { + "Name": "current", + "Order": 0, + "RowNr": 30, + "DisplayName": "Stromstärke: ", + "UnitOfMeasurement": " A", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none", + "Precision": 2 + }, + "voltage": { + "Name": "voltage", + "Order": 0, + "RowNr": 40, + "DisplayName": "Spannung: ", + "UnitOfMeasurement": " V", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none", + "Precision": 0 + }, + "energy": { + "Name": "energy", + "Order": 0, + "RowNr": 50, + "DisplayName": "Historischer Verbrauch: ", + "UnitOfMeasurement": " kWh", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none" + }, + "occupancy": { + "Name": "occupancy", + "Order": 0, + "RowNr": 10, + "DisplayName": "Anwesenheit: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + } + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/ContactSensor.json b/AppBrokerASP/DeviceLayouts/ContactSensor.json index 61af0fb..6047c69 100644 --- a/AppBrokerASP/DeviceLayouts/ContactSensor.json +++ b/AppBrokerASP/DeviceLayouts/ContactSensor.json @@ -1,127 +1,37 @@ { - "Version": 2, + "Version": 3, "UniqueName": "ContactSensorLayout", - "TypeNames": [ "SNZB-04", "WL-19DWZ" ], - "IconName": "XiaomiTempSensor", - "NotificationSetup": [ - { - "UniqueName": "DingDongNotification", - "TranslatableName": "Klingel", - "Global": false - }, - { - "UniqueName": "Fridge15MinutesNotification", - "TranslatableName": "Über 15 Minuten geöffnet", - "Global": false - } + "IconName": "Paw", + "TypeNames": [ + "SNZB-04", + "WL-19DWZ" ], "DashboardDeviceLayout": { "DashboardProperties": [ { - "Name": "contact", - "Order": 2, - "RowNr": 1, - "EditInfo": { - "EditType": "toggle", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "zigbee", - "Value": true, - "Parameters": [ - ] - }, - { - "Command": "zigbee", - "Value": false, - "Parameters": [ - ] - } - ], - "Display": "Status", - "ActiveValue": false - } + "@ref": "$['Base.json'].dashboard.contact" }, { - "Name": "id", - "Order": 2, - "RowNr": 2, - "Hex": true, - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].dashboard.id" } ] }, "DetailDeviceLayout": { "PropertyInfos": [ { - "Name": "friendlyName", - "Order": 0, - "RowNr": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.friendly_name" }, { - "Name": "id", - "Order": 1, - "RowNr": 1, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].detail.id" }, { - "Name": "contact", - "Order": 0, - "RowNr": 2, - "DisplayName": "Geoeffnet: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.contact" }, { - "Name": "lastReceived", - "Order": 0, - "RowNr": 6, - "DisplayName": "Zuletzt empfangen: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "Format": "dd.MM.yyyy HH:mm:ss" + "@ref": "$['Base.json'].detail.last_received" }, { - "Name": "linkQuality", - "Order": 0, - "RowNr": 7, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.link_quality" } ], "TabInfos": [ diff --git a/AppBrokerASP/DeviceLayouts/DemoWidget - Kopie.json b/AppBrokerASP/DeviceLayouts/DemoWidget - Kopie.json index a26996f..c834a70 100644 --- a/AppBrokerASP/DeviceLayouts/DemoWidget - Kopie.json +++ b/AppBrokerASP/DeviceLayouts/DemoWidget - Kopie.json @@ -120,7 +120,7 @@ "Name": "id", "Order": 2, "RowNr": 3, - "Hex": true, + "Hex": true, "ShowOnlyInDeveloperMode": true } ] @@ -150,17 +150,7 @@ "Expanded": true }, { - "Name": "friendlyName", - "Order": 0, - "RowNr": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.friendly_name" }, // { // "Name": "id", @@ -201,7 +191,7 @@ "Parameters": [ "auto" ] - }, + } // { // "Name": "icon", // "CodePoint": 62757, @@ -443,4 +433,8 @@ "TranslatableName": "Wir deaktiviert" } ] -} \ No newline at end of file +} + + + + \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/Doorbell.json b/AppBrokerASP/DeviceLayouts/Doorbell.json new file mode 100644 index 0000000..c2ef696 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/Doorbell.json @@ -0,0 +1,74 @@ +{ + "Version": 1, + "UniqueName": "DoorbellLayout", + "IconName": "Paw", + "TypeNames": [ + "Doorbell" + ], + "NotificationSetup": [ + { + "UniqueName": "DingDongNotification", + "TranslatableName": "Klingel", + "Global": true + } + ], + "DashboardDeviceLayout": { + "DashboardProperties": [ + { + "@ref": "$['ZigbeeBase.json'].dashboard.toggle" + } + { + "@ref": "$['Base.json'].dashboard.id" + } + ] + }, + "DetailDeviceLayout": { + "PropertyInfos": [ + { + "Name": "state", + "Order": 0, + "RowNr": 2, + "DisplayName": "Klingel: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none" + }, + { + "@ref": "$['Base.json'].detail.friendly_name" + }, + { + "@ref": "$['Base.json'].detail.id" + }, + { + "@ref": "$['Base.json'].detail.last_received" + }, + { + "@ref": "$['Base.json'].detail.link_quality" + } + ], + "TabInfos": [ + { + "Id": 0, + "IconName": "home", + "Order": 1, + "LinkedDevice": null + } + ], + "HistoryProperties": [ + { + "PropertyName": "state", + "XAxisName": "An / Aus", + "UnitOfMeasurement": "", + "IconName": "power-swtich-59454", + "BrightThemeColor": 4292149248, + "DarkThemeColor": 4294922834, + "ChartType": "step" + } + ] + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/Empty.json b/AppBrokerASP/DeviceLayouts/Empty.json new file mode 100644 index 0000000..e859d8a --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/Empty.json @@ -0,0 +1,87 @@ +{ + "UniqueName": "DemoLayout", + "IconName": "Paw", + "TypeNames": [ + "E1743", "929002398602" + ], + "DashboardDeviceLayout": { + "DashboardProperties": [ + { + "Name": "Id", + "Order": 0, + "Format": "", + "EditInfo": null, + "TextStyle": { + "FontSize": 22.0, + "FontFamily": "FontName" + }, + "RowNr": null, + "ShowOnlyInDeveloperMode": null, + "Hex": true + }, + { + "Name": "battery", + "Order": 0, + "EditInfo": { + "EditType": "icon", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "None", + "Value": 0, + "Min": 0, + "Max": 20, + "CodePoint": 59412, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 0, + "Min": 20, + "Max": 40, + "CodePoint": 59408, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 0, + "Min": 40, + "Max": 60, + "CodePoint": 59409, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 0, + "Min": 60, + "Max": 80, + "CodePoint": 59410, + "FontFamily": "Smarthome" + }, + { + "Command": "None", + "Value": 100, + "Min": 80, + "Max": 101, + "CodePoint": 59411, + "FontFamily": "Smarthome" + } + ] + }, + "SpecialType": "right" + }, + ] + }, + "DetailDeviceLayout": { + "PropertyInfos": [], + "TabInfos": [ + { + "Id": 0, + "IconName": "DefaultIcon", + "Order": 1, + "LinkedDevice": null + }, + ], + "HistoryProperties": [] + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/HistoryBase.json b/AppBrokerASP/DeviceLayouts/HistoryBase.json new file mode 100644 index 0000000..ef435d5 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/HistoryBase.json @@ -0,0 +1,92 @@ +{ + "load_power": { + "PropertyName": "loadPower", + "XAxisName": "Verbrauch", + "UnitOfMeasurement": " W", + "IconName": "XiaomiTempSensor", + "BrightThemeColor": 4292149248, + "DarkThemeColor": 4294922834, + "ChartType": "step" + }, + "current": { + "PropertyName": "current", + "XAxisName": "Stromstärke", + "UnitOfMeasurement": " A", + "IconName": "cloud", + "BrightThemeColor": 4280902399, + "DarkThemeColor": 4286755327, + "ChartType": "step" + }, + "energy": { + "PropertyName": "energy", + "XAxisName": "Historischer Verbrauch", + "UnitOfMeasurement": " kWh", + "IconName": "droplet-59403", + "BrightThemeColor": 4278241363, + "DarkThemeColor": 4278249078, + "ChartType": "line" + }, + "voltage": { + "PropertyName": "voltage", + "XAxisName": "Spannung", + "UnitOfMeasurement": " V", + "IconName": "flash-59419", + "BrightThemeColor": 4278241363, + "DarkThemeColor": 4278249078, + "ChartType": "step" + }, + "state": { + "PropertyName": "state", + "XAxisName": "Status", + "UnitOfMeasurement": " ", + "IconName": "celcius-59424", + "BrightThemeColor": 4278241363, + "DarkThemeColor": 4278249078, + "ChartType": "step" + }, + "contact": { + "PropertyName": "contact", + "XAxisName": "Kontakt", + "UnitOfMeasurement": "", + "IconName": "power-swtich-59454", + "BrightThemeColor": 4292149248, + "DarkThemeColor": 4294922834, + "ChartType": "step" + }, + "occupancy": { + "PropertyName": "occupancy", + "XAxisName": "Anwesenheit", + "UnitOfMeasurement": "", + "IconName": "power-swtich-59454", + "BrightThemeColor": 4292149248, + "DarkThemeColor": 4294922834, + "ChartType": "step" + }, + "temperature": { + "PropertyName": "temperature", + "XAxisName": "Temperatur", + "UnitOfMeasurement": " °C", + "IconName": "XiaomiTempSensor", + "BrightThemeColor": 4292149248, + "DarkThemeColor": 4294922834, + "ChartType": "line" + }, + "humidity": { + "PropertyName": "humidity", + "XAxisName": "rel. Luftfeuchtigkeit", + "UnitOfMeasurement": " %", + "IconName": "cloud", + "BrightThemeColor": 4280902399, + "DarkThemeColor": 4286755327, + "ChartType": "line" + }, + "pressure": { + "PropertyName": "pressure", + "XAxisName": "Luftdruck", + "UnitOfMeasurement": " kPA", + "IconName": "barometer", + "BrightThemeColor": 4278241363, + "DarkThemeColor": 4278249078, + "ChartType": "line" + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/Lampe - AB32840.json b/AppBrokerASP/DeviceLayouts/Lampe - AB32840.json index 85dda7d..cff6c18 100644 --- a/AppBrokerASP/DeviceLayouts/Lampe - AB32840.json +++ b/AppBrokerASP/DeviceLayouts/Lampe - AB32840.json @@ -1,102 +1,34 @@ { - "Version": 1, + "Version": 3, "UniqueName": "Osram Lampe Lightify", - "IconName": "XiaomiTempSensor", + "IconName": "AB32840", "TypeNames": [ - "AB32840" + "AB32840QQQ" ], "DashboardDeviceLayout": { "DashboardProperties": [ { - "Name": "state", - "Order": 2, - "RowNr": 1, - "EditInfo": { - "EditType": "toggle", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "zigbee", - "Value": true, - "Parameters": [ - "Light.state", - "ON" - ] - }, - { - "Command": "zigbee", - "Value": false, - "Parameters": [ - "Light.state", - "OFF" - ] - } - ], - "Display": "Status", - "ActiveValue": true - } + "@ref": "$['ZigbeeBase.json'].dashboard.light" }, { - "Name": "id", - "Order": 4, - "RowNr": 4, - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].dashboard.id" } ] }, "DetailDeviceLayout": { "PropertyInfos": [ { - "Name": "friendlyName", - "RowNr": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.friendly_name" }, { - "Name": "id", - "RowNr": 1, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].detail.id" }, { - "Name": "state", - "RowNr": 2, - "DisplayName": "Status: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.light_state" }, { - "Name": "color_temp", - "RowNr": 3, - "Order": 0, - "DisplayName": "Farbtemperatur: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.color_temp_label" }, - { "Name": "color_temp", "RowNr": 3, @@ -104,7 +36,7 @@ "Expanded": true, "EditInfo": { "EditType": "slider", - "EditCommand": "Update", + "MessageType": "Update", "EditParameter": [ { "Command": 151, @@ -138,58 +70,16 @@ "Precision": 0 }, { - "Name": "brightness", - "RowNr": 5, - "Order": 0, - "DisplayName": "Helligkeit: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.brightness_label" }, { - "Name": "brightness", - "RowNr": 5, - "Order": 0, - "Expanded": true, - "EditInfo": { - "EditType": "slider", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": 151, - "Parameters": [ - "brightness" - ], - "Value": { - "Min": 0.0, - "Max": 254.0, - "Divisions": 254 - } - } - ], - "Display": "Helligkeit", - "ActiveValue": true, - "GradientColors": [ - [ - 255, - 60, - 60, - 60 - ], - [ - 255, - 255, - 255, - 255 - ] - ] - }, - "Precision": 0 + "@ref": "$['ZigbeeBase.json'].detail.brightness" + }, + { + "@ref": "$['Base.json'].detail.link_quality" + }, + { + "@ref": "$['ZigbeeBase.json'].detail.light_state_fab" }, { "Name": "123_Invalid", @@ -203,7 +93,7 @@ "Order": 2, "EditInfo": { "EditType": "dropdown", - "EditCommand": "Update", + "MessageType": "Update", "EditParameter": [ { "Command": 150, @@ -244,24 +134,12 @@ ] } }, - { - "Name": "linkQuality", - "RowNr": 11, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" - }, // { // "Name": "effect", // "RowNr": 6, // "EditInfo": { // "EditType": "dropdown", - // "EditCommand": "Update", + // "MessageType": "Update", // "EditParameter": [ // { // "Command": 404, @@ -308,55 +186,7 @@ // "Display": "Status DropDown" // } // }, - { - "Name": "state", - "RowNr": 100, - "EditInfo": { - "EditType": "FloatingActionButton", - "EditCommand": "Update", - "EditParameter": [ - { - "CodePoint": 58595, - "Command": 150, - "Value": true, - "Parameters": [ - "state", - "OFF" - ] - }, - { - "Command": 150, - "CodePoint": 58595, - "Value": false, - "Parameters": [ - "state", - "ON" - ] - } - // { - // "Command": "zigbee", - // "Value": false, - // "CodePoint": 58595, - // "Parameters": [ - // "Light.state", - // "ON" - // ] - // }, - // { - // "Command": "zigbee", - // "Value": true, - // "CodePoint": 58595, - // "Parameters": [ - // "Light.state", - // "OFF" - // ] - // } - ] - } - } ], - "HistoryProperties": [ - - ] + "HistoryProperties": [] } } \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/Lampe - Sascha Schlafzimmer.json b/AppBrokerASP/DeviceLayouts/Lampe - Sascha Schlafzimmer.json index 2f4b3d6..e0f4cbb 100644 --- a/AppBrokerASP/DeviceLayouts/Lampe - Sascha Schlafzimmer.json +++ b/AppBrokerASP/DeviceLayouts/Lampe - Sascha Schlafzimmer.json @@ -1,193 +1,42 @@ { "Version": 2, "UniqueName": "Sascha Schlafzimmer Lampe", - "IconName": "XiaomiTempSensor", - "Ids": [ -4885370955287228666 ], + "IconName": "AB32840", + "Ids": [ + -4885370955287228666 + ], "DashboardDeviceLayout": { "DashboardProperties": [ { - "Name": "state", - "Order": 2, - "RowNr": 1, - "EditInfo": { - "EditType": "toggle", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "zigbee", - "Value": true, - "Parameters": [ - "Light.state", - "ON" - ] - }, - { - "Command": "zigbee", - "Value": false, - "Parameters": [ - "Light.state", - "OFF" - ] - } - ], - "Display": "Status", - "ActiveValue": true - } + "@ref": "$['ZigbeeBase.json'].dashboard.light" }, { - "Name": "id", - "Order": 4, - "RowNr": 4, - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].dashboard.id" } ] }, "DetailDeviceLayout": { "PropertyInfos": [ { - "Name": "friendlyName", - "RowNr": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.friendly_name" }, { - "Name": "id", - "RowNr": 1, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].detail.id" }, { - "Name": "state", - "RowNr": 2, - "DisplayName": "Status: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.light_state" }, { - "Name": "color_temp", - "RowNr": 3, - "Order": 0, - "DisplayName": "Farbtemperatur: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.color_temp_label" }, - { - "Name": "color_temp", - "RowNr": 4, - "Order": 0, - "Expanded": true, - "EditInfo": { - "EditType": "slider", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": 151, - "Parameters": [ - "color_temp" - ], - "Value": { - "Min": 250.0, - "Max": 454.0, - "Divisions": 204 - } - } - ], - "Display": "Farbtemperatur", - "ActiveValue": true, - "GradientColors": [ - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 230, - 160, - 128 - ] - ] - }, - "Precision": 0 + "@ref": "$['ZigbeeBase.json'].detail.color_temp" }, { - "Name": "brightness", - "RowNr": 5, - "Order": 0, - "DisplayName": "Helligkeit: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.brightness_label" }, { - "Name": "brightness", - "RowNr": 6, - "Order": 0, - "Expanded": true, - "EditInfo": { - "EditType": "slider", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": 151, - "Parameters": [ - "brightness" - ], - "Value": { - "Min": 0.0, - "Max": 254.0, - "Divisions": 254 - } - } - ], - "Display": "Helligkeit", - "ActiveValue": true, - "GradientColors": [ - [ - 255, - 60, - 60, - 60 - ], - [ - 255, - 255, - 255, - 255 - ] - ] - }, - "Precision": 0 + "@ref": "$['ZigbeeBase.json'].detail.brightness" }, { "Name": "123_Invalid", @@ -201,7 +50,7 @@ "Order": 2, "EditInfo": { "EditType": "dropdown", - "EditCommand": "Update", + "MessageType": "Update", "EditParameter": [ { "Command": 150, @@ -242,36 +91,15 @@ ] } }, - // { - // "Name": "lastReceived", - // "RowNr": 10, - // "DisplayName": "Zuletzt empfangen: ", - // "TextStyle": { - // "FontSize": 16.0, - // "FontFamily": "FontName", - // "FontWeight": "normal", - // "FontStyle": "normal" - // }, - // "SpecialType": "none" - // }, { - "Name": "linkQuality", - "RowNr": 11, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.link_quality" }, // { // "Name": "effect", // "RowNr": 6, // "EditInfo": { // "EditType": "dropdown", - // "EditCommand": "Update", + // "MessageType": "Update", // "EditParameter": [ // { // "Command": 404, @@ -319,61 +147,12 @@ // } // }, { - "Name": "state", - "RowNr": 100, - "EditInfo": { - "EditType": "FloatingActionButton", - "EditCommand": "Update", - "EditParameter": [ - { - "CodePoint": 58595, - "Command": 150, - "Value": true, - "Parameters": [ - "state", - "OFF" - ] - }, - { - "Command": 150, - "CodePoint": 58595, - "Value": false, - "Parameters": [ - "state", - "ON" - ] - } - // { - // "Command": "zigbee", - // "Value": false, - // "CodePoint": 58595, - // "Parameters": [ - // "Light.state", - // "ON" - // ] - // }, - // { - // "Command": "zigbee", - // "Value": true, - // "CodePoint": 58595, - // "Parameters": [ - // "Light.state", - // "OFF" - // ] - // } - ] - } + "@ref": "$['ZigbeeBase.json'].detail.light_state_fab" } ], "HistoryProperties": [ { - "PropertyName": "state", - "XAxisName": "Status", - "UnitOfMeasurement": " ", - "IconName": "flash-59419", - "BrightThemeColor": 4278241363, - "DarkThemeColor": 4278249078, - "ChartType": "step" + "@ref": "$['HistoryBase.json'].state" } ] } diff --git a/AppBrokerASP/DeviceLayouts/Lampe.json b/AppBrokerASP/DeviceLayouts/Lampe.json index 3f673a0..adffba8 100644 --- a/AppBrokerASP/DeviceLayouts/Lampe.json +++ b/AppBrokerASP/DeviceLayouts/Lampe.json @@ -1,10 +1,10 @@ { - "Version": 13, + "Version": 15, "UniqueName": "Ikea Lampe1", - "IconName": "XiaomiTempSensor", + "IconName": "L1529", "TypeNames": [ "LED1624G9", - "LED1732G11", + "LED1723G11", "T2011", "L1529", "FloaltPanel", @@ -13,193 +13,41 @@ "DashboardDeviceLayout": { "DashboardProperties": [ { - "Name": "state", - "Order": 2, - "RowNr": 1, - "EditInfo": { - "EditType": "toggle", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "zigbee", - "Value": true, - "Parameters": [ - "Light.state", - "ON" - ] - }, - { - "Command": "zigbee", - "Value": false, - "Parameters": [ - "Light.state", - "OFF" - ] - } - ], - "Display": "Status", - "ActiveValue": true - } + "@ref": "$['ZigbeeBase.json'].dashboard.light" }, { - "Name": "id", - "Order": 4, - "RowNr": 4, - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].dashboard.id" } ] }, "DetailDeviceLayout": { "PropertyInfos": [ { - "Name": "friendlyName", - "RowNr": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.friendly_name" }, { - "Name": "id", - "RowNr": 1, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].detail.id" }, { - "Name": "state", - "RowNr": 2, - "DisplayName": "Status: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.light_state" }, { - "Name": "color_temp", - "RowNr": 3, - "Order": 0, - "DisplayName": "Farbtemperatur: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.color_temp_label" }, - { - "Name": "color_temp", - "RowNr": 3, - "Order": 0, - "Expanded": true, - "EditInfo": { - "EditType": "slider", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": 151, - "Parameters": [ - "color_temp" - ], - "Value": { - "Min": 250.0, - "Max": 454.0, - "Divisions": 204 - } - } - ], - "Display": "Farbtemperatur", - "ActiveValue": true, - "GradientColors": [ - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 230, - 160, - 128 - ] - ] - }, - "Precision": 0 + "@ref": "$['ZigbeeBase.json'].detail.color_temp" }, { - "Name": "brightness", - "RowNr": 5, - "Order": 0, - "DisplayName": "Helligkeit: ", - "UnitOfMeasurement": "", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['ZigbeeBase.json'].detail.brightness_label" }, { - "Name": "brightness", - "RowNr": 5, - "Order": 0, - "Expanded": true, - "EditInfo": { - "EditType": "slider", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": 151, - "Parameters": [ - "brightness" - ], - "Value": { - "Min": 0.0, - "Max": 254.0, - "Divisions": 254 - } - } - ], - "Display": "Helligkeit", - "ActiveValue": true, - "GradientColors": [ - [ - 255, - 60, - 60, - 60 - ], - [ - 255, - 255, - 255, - 255 - ] - ] - }, - "Precision": 0 + "@ref": "$['ZigbeeBase.json'].detail.brightness" + }, + { + "@ref": "$['Base.json'].detail.link_quality" }, { - "Name": "123_Invalid", - "DisplayName": "Verhalten beim Start: ", - "RowNr": 6 + "@ref": "$['ZigbeeBase.json'].detail.light_state_fab" }, { "Name": "power_on_behavior", @@ -208,7 +56,7 @@ "Order": 2, "EditInfo": { "EditType": "dropdown", - "EditCommand": "Update", + "MessageType": "Update", "EditParameter": [ { "Command": 150, @@ -250,35 +98,11 @@ } }, // { - // "Name": "lastReceived", - // "RowNr": 10, - // "DisplayName": "Zuletzt empfangen: ", - // "TextStyle": { - // "FontSize": 16.0, - // "FontFamily": "FontName", - // "FontWeight": "normal", - // "FontStyle": "normal" - // }, - // "SpecialType": "none" - // }, - { - "Name": "linkQuality", - "RowNr": 11, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" - }, - // { // "Name": "effect", // "RowNr": 6, // "EditInfo": { // "EditType": "dropdown", - // "EditCommand": "Update", + // "MessageType": "Update", // "EditParameter": [ // { // "Command": 404, @@ -324,55 +148,8 @@ // ], // "Display": "Status DropDown" // } - // }, - { - "Name": "state", - "RowNr": 100, - "EditInfo": { - "EditType": "FloatingActionButton", - "EditCommand": "Update", - "EditParameter": [ - { - "CodePoint": 58595, - "Command": 150, - "Value": true, - "Parameters": [ - "state", - "OFF" - ] - }, - { - "Command": 150, - "CodePoint": 58595, - "Value": false, - "Parameters": [ - "state", - "ON" - ] - } - // { - // "Command": "zigbee", - // "Value": false, - // "CodePoint": 58595, - // "Parameters": [ - // "Light.state", - // "ON" - // ] - // }, - // { - // "Command": "zigbee", - // "Value": true, - // "CodePoint": 58595, - // "Parameters": [ - // "Light.state", - // "OFF" - // ] - // } - ] - } - } + // } ], - "HistoryProperties": [ - ] + "HistoryProperties": [] } } \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/LampeFarbig.json b/AppBrokerASP/DeviceLayouts/LampeFarbig.json index 247e78a..38fedc6 100644 --- a/AppBrokerASP/DeviceLayouts/LampeFarbig.json +++ b/AppBrokerASP/DeviceLayouts/LampeFarbig.json @@ -16,13 +16,9 @@ "RowNr": 0, "UnitOfMeasurement": " K" }, - { - "Name": "id", - "Order": 2, - "RowNr": 2, - "ShowOnlyInDeveloperMode": true - } + "@ref": "$['Base.json'].dashboard.id" + }, ] }, "DetailDeviceLayout": { @@ -52,7 +48,6 @@ "SpecialType": "none", "ShowOnlyInDeveloperMode": true }, - { "Name": "colorMode", "Order": 0, @@ -67,7 +62,6 @@ "TabInfoId": 0, "SpecialType": "none" }, - { "Name": "state", "Order": 0, @@ -136,6 +130,9 @@ }, "TabInfoId": 0, "SpecialType": "none" + }, + { + "@ref": "$['ZigbeeBase.json'].detail.light_state_fab" } ], "TabInfos": [ @@ -145,10 +142,7 @@ "Order": 1, "LinkedDevice": null } - ], - "HistoryProperties": [ - - ] + "HistoryProperties": [] } } \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/Lampe_Tuya.json b/AppBrokerASP/DeviceLayouts/Lampe_Tuya.json new file mode 100644 index 0000000..e46aa21 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/Lampe_Tuya.json @@ -0,0 +1,102 @@ +{ + "Version": 3, + "UniqueName": "Tuya Lampe", + "IconName": "L1529", + "TypeNames": [ + "CK-BL702-AL-01_1" + ], + "DashboardDeviceLayout": { + "DashboardProperties": [ + { + "@ref": "$['ZigbeeBase.json'].dashboard.light" + }, + { + "@ref": "$['Base.json'].dashboard.id" + } + ] + }, + "DetailDeviceLayout": { + "PropertyInfos": [ + { + "@ref": "$['Base.json'].detail.friendly_name" + }, + { + "@ref": "$['Base.json'].detail.id" + }, + { + "@ref": "$['ZigbeeBase.json'].detail.light_state" + }, + { + "@ref": "$['ZigbeeBase.json'].detail.color_temp_label" + }, + { + "@ref": "$['TuyaBase.json'].detail.color_temp" + }, + { + "@ref": "$['ZigbeeBase.json'].detail.brightness_label" + }, + { + "@ref": "$['ZigbeeBase.json'].detail.brightness" + }, + { + "@ref": "$['Base.json'].detail.link_quality" + }, + { + "@ref": "$['ZigbeeBase.json'].detail.light_state_fab" + } + // { + // "Name": "effect", + // "RowNr": 6, + // "EditInfo": { + // "EditType": "dropdown", + // "MessageType": "Update", + // "EditParameter": [ + // { + // "Command": 404, + // "Value": "", + // "DisplayName": "---" + // }, + // { + // "Command": 400, + // "Value": "blink", + // "DisplayName": "Blinken", + // "Parameters": [ "blink" ] + // }, + // { + // "Command": 400, + // "Value": "breathe", + // "DisplayName": "Atmen", + // "Parameters": [ "breathe" ] + // }, + // { + // "Command": 400, + // "Value": "okay", + // "DisplayName": "okay", + // "Parameters": [ "okay" ] + // }, + // { + // "Command": 400, + // "Value": "channel_change", + // "DisplayName": "Channelwechsel", + // "Parameters": [ "channel_change" ] + // }, + // { + // "Command": 400, + // "Value": "finish_effect", + // "DisplayName": "Fertig", + // "Parameters": [ "finish_effect" ] + // }, + // { + // "Command": 400, + // "Value": "stop_effect", + // "DisplayName": "Stop", + // "Parameters": [ "stop_effect" ] + // } + // ], + // "Display": "Status DropDown" + // } + // }, + ], + "HistoryProperties": [] + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/OsramPlug.json b/AppBrokerASP/DeviceLayouts/OsramPlug.json new file mode 100644 index 0000000..bdc785a --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/OsramPlug.json @@ -0,0 +1,57 @@ +{ + "Version": 2, + "UniqueName": "OsramPlugLayout", + "IconName": "TuyaSwitchSensor", + "TypeNames": [ + "OsramPlug" + ], + "DashboardDeviceLayout": { + "DashboardProperties": [ + { + "@ref": "$['ZigbeeBase.json'].dashboard.toggle" + }, + { + "@ref": "$['Base.json'].dashboard.id" + } + ] + }, + "DetailDeviceLayout": { + "PropertyInfos": [ + { + "@ref": "$['Base.json'].detail.friendly_name" + }, + { + "@ref": "$['Base.json'].detail.id" + }, + { + "@ref": "$['Base.json'].detail.contact" + }, + { + "@ref": "$['Base.json'].detail.last_received" + }, + { + "@ref": "$['Base.json'].detail.link_quality" + } + { + "Name": "_", + "Order": 0, + "RowNr": 33, + "DisplayName": "Status: " + }, + { + "@ref": "$['ZigbeeBase.json'].detail.toggle" + } + ], + "HistoryProperties": [ + { + "PropertyName": "state", + "XAxisName": "Status", + "UnitOfMeasurement": " ", + "IconName": "celcius-59424", + "BrightThemeColor": 4278241363, + "DarkThemeColor": 4278249078, + "ChartType": "step" + } + ] + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/Osvalla.json b/AppBrokerASP/DeviceLayouts/Osvalla.json deleted file mode 100644 index 7c45ac3..0000000 --- a/AppBrokerASP/DeviceLayouts/Osvalla.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "UniqueName": "OsvallaPanelLayout", - "TypeName": "OsvallaPanel", - "IconName": "XiaomiTempSensor", - "Ids": [], - "DashboardDeviceLayout": { - "DashboardProperties": [ - { - "Name": "temperature", - "Order": 0, - "TextStyle": { - "FontSize": 24.0, - "FontWeight": "bold" - }, - "RowNr": 0, - "UnitOfMeasurement": " °C" - }, - { - "Name": "humidity", - "Order": 1, - "RowNr": 1, - "TextStyle": { - "FontSize": 14.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " %" - }, - { - "Name": "pressure", - "Order": 2, - "RowNr": 1, - "TextStyle": { - "FontSize": 14.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " kpa" - }, - { - "Name": "id", - "Order": 2, - "RowNr": 2, - "ShowOnlyInDeveloperMode": true - } - ] - }, - "DetailDeviceLayout": { - "PropertyInfos": [ - { - "Name": "friendlyName", - "Order": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "id", - "Order": 1, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true - }, - { - "Name": "temperature", - "Order": 0, - "DisplayName": "Temperature: ", - "UnitOfMeasurement": " °C", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "humidity", - "Order": 0, - "DisplayName": "Luftfeuchtigkeit: ", - "UnitOfMeasurement": " %", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "pressure", - "Order": 0, - "DisplayName": "Luftdruck: ", - "UnitOfMeasurement": " kPA", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "battery", - "Order": 0, - "DisplayName": "Battery: ", - "UnitOfMeasurement": " %", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "lastReceivedFormatted", - "Order": 0, - "DisplayName": "Zuletzt empfangen: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "Format": "dd" - }, - { - "Name": "link_Quality", - "Order": 0, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - } - ], - "TabInfos": [ - { - "Id": 0, - "IconName": "home", - "Order": 1, - "LinkedDevice": null - }, - { - "Id": 1, - "IconName": "off-59399", - "Order": 2, - "LinkedDevice": null - } - ], - "HistoryProperties": [ - { - "PropertyName": "temperature", - "XAxisName": "Temperatur", - "UnitOfMeasurement": " °C", - "IconName": "XiaomiTempSensor", - "BrightThemeColor": 4292149248, - "DarkThemeColor": 4294922834, - "ChartType": "line" - }, - { - "PropertyName": "humidity", - "XAxisName": "rel. Luftfeuchtigkeit", - "UnitOfMeasurement": " %", - "IconName": "cloud", - "BrightThemeColor": 4280902399, - "DarkThemeColor": 4286755327, - "ChartType": "line" - }, - { - "PropertyName": "pressure", - "XAxisName": "Luftdruck", - "UnitOfMeasurement": " kPA", - "IconName": "barometer", - "BrightThemeColor": 4278241363, - "DarkThemeColor": 4278249078, - "ChartType": "line" - } - ] - } -} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/PresenceSensor.json b/AppBrokerASP/DeviceLayouts/PresenceSensor.json new file mode 100644 index 0000000..1af1cf2 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/PresenceSensor.json @@ -0,0 +1,50 @@ +{ + "Version": 3, + "UniqueName": "PresenceSensorLayout", + "IconName": "Paw", + "TypeNames": [ + "SNZB-06P" + ], + "DashboardDeviceLayout": { + "DashboardProperties": [ + { + "@ref": "$['Base.json'].dashboard.occupancy" + }, + { + "@ref": "$['Base.json'].dashboard.id" + } + ] + }, + "DetailDeviceLayout": { + "PropertyInfos": [ + { + "@ref": "$['Base.json'].detail.friendly_name" + }, + { + "@ref": "$['Base.json'].detail.id" + }, + { + "@ref": "$['Base.json'].detail.last_received" + }, + { + "@ref": "$['Base.json'].detail.link_quality" + }, + { + "@ref": "$['Base.json'].detail.occupancy" + } + ], + "TabInfos": [ + { + "Id": 0, + "IconName": "home", + "Order": 1, + "LinkedDevice": null + } + ], + "HistoryProperties": [ + { + "@ref": "$['HistoryBase.json'].occupancy" + } + ] + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/SPZB0001.json b/AppBrokerASP/DeviceLayouts/SPZB0001.json deleted file mode 100644 index fcd0f5d..0000000 --- a/AppBrokerASP/DeviceLayouts/SPZB0001.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "UniqueName": "SPZB0001", - "TypeNames": [ "SPZB0001" ], - "IconName": "XiaomiTempSensor", - "DashboardDeviceLayout": { - "DashboardProperties": [ - { - "Name": "available", - "EditParameter": [], - "EditInfo": { - "EditType": "button", - "Dialog": "HeaterConfig", - "Display": "Heizplan einstellen" - } - }, - { - "Name": "heatingSetpntCurrent", - "RowNr": 2, - "UnitOfMeasurement": " °C" - }, - { - "Name": "valvePosition", - "RowNr": 3, - "UnitOfMeasurement": " %" - }, - { - "Name": "battery", - "RowNr": 3, - "UnitOfMeasurement": " %" - }, - { - "Name": "id", - "Order": 5, - "RowNr": 5, - "ShowOnlyInDeveloperMode": true - } - ] - } -} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/SampleLayout.json b/AppBrokerASP/DeviceLayouts/SampleLayout.json index bc94ed3..82b9e73 100644 --- a/AppBrokerASP/DeviceLayouts/SampleLayout.json +++ b/AppBrokerASP/DeviceLayouts/SampleLayout.json @@ -1,7 +1,7 @@ { "UniqueName": "DemoLayout", + "IconName": "Paw", "TypeName": "Demo", - "IconName": "XiaomiTempSensor", "Ids": [ -1, -2 diff --git a/AppBrokerASP/DeviceLayouts/SuschHeater.json b/AppBrokerASP/DeviceLayouts/SuschHeater.json new file mode 100644 index 0000000..0a7e7c7 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/SuschHeater.json @@ -0,0 +1,438 @@ +{ + "Version": 4, + "UniqueName": "SuschHeater", + "IconName": "XiaomiTempSensor", + "TypeNames": [ + "SuschHeater" + ], + "DashboardDeviceLayout": { + "DashboardProperties": [ + { + "Name": "current_target_temp", + "Order": 0, + "RowNr": 0, + "TextStyle": { + "FontSize": 22.0, + "FontFamily": "FontName", + "FontWeight": "bold", + "FontStyle": "normal" + }, + "UnitOfMeasurement": " °C" + }, + { + "Name": "current_target_time", + "Order": 0, + "RowNr": 1, + "Format": "HH:mm", + "UnitOfMeasurement": " Uhr" + }, + { + "Name": "system_mode", + "Order": 0, + "EditInfo": { + "EditType": "icon", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "None", + "Value": "off", + "CodePoint": 62148, + "FontFamily": "MaterialIcons" + }, + { + "Command": "None", + "Value": "heat", + "Disable": true + }, + { + "Command": "None", + "Value": "auto", + "Disable": true + } + ] + }, + "SpecialType": "right" + }, + { + "Name": "running_mode", + "Order": 0, + "RowNr": 0, + "EditInfo": { + "EditType": "icon", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "None", + "Value": "off", + "Disable": true + }, + { + "Command": "None", + "Value": "heat", + "CodePoint": 984437, + "FontFamily": "MaterialIcons", + "Size": 18.0 + }, + { + "Command": "None", + "Value": "cool", + "CodePoint": 57399, + "FontFamily": "MaterialIcons", + "Size": 18.0 + } + ] + }, + "SpecialType": "right" + }, + { + "Name": "used_temperature_source", + "Order": 0, + "EditInfo": { + "EditType": "icon", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "None", + "Value": "none", + "CodePoint": 983712, + "FontFamily": "MaterialIcons", + "Color": 4294901760 + }, + { + "Command": "None", + "Value": "local", + "CodePoint": 983712, + "FontFamily": "MaterialIcons", + "Color": 4294967040 + }, + { + "Command": "None", + "Value": "remote", + "CodePoint": 57399, + "FontFamily": "MaterialIcons", + "Disable": true + } + ] + }, + "SpecialType": "right" + }, + { + "Name": "id", + "Order": 2, + "RowNr": 3, + "Hex": true, + "ShowOnlyInDeveloperMode": true + } + ] + }, + "DetailDeviceLayout": { + "PropertyInfos": [ + { + "Name": "id", + "Order": 1, + "RowNr": 60, + "EditParameter": [], + "EditInfo": { + "EditType": "button", + "Dialog": "HeaterConfig", + "Display": "Heizplan einstellen", + "EditParameter": [ + { + "Name": "icon", + "CodePoint": 58751, + "FontFamily": "MaterialIcons" + } + ] + }, + "TabInfoId": 0, + "SpecialType": "none", + "blurryCard": true, + "Expanded": true, + "Hex": true + }, + { + "@ref": "$['Base.json'].detail.friendly_name" + }, + // { + // "Name": "id", + // "Order": 1, + // "RowNr": 1, + // "TextStyle": { + // "FontSize": 16.0, + // "FontFamily": "FontName", + // "FontWeight": "bold", + // "FontStyle": "normal" + // }, + // "TabInfoId": 0, + // "SpecialType": "none", + // "ShowOnlyInDeveloperMode": true + // }, + { + "Name": "system_mode", + "Order": 1, + "RowNr": 50, + "EditParameter": [], + "EditInfo": { + "EditType": "Toggle", + "MessageType": "Update", + "ActiveValue": "auto", + "EditParameter": [ + { + "Command": 10, + "Value": "off", + "DisplayName": "Heizung: ", + "Parameters": [ + "auto" + ] + }, + { + "Command": 10, + "Value": "auto", + "DisplayName": "Heizung: ", + "Parameters": [ + "off" + ] + }, + // { + // "Name": "icon", + // "CodePoint": 62757, + // "FontFamily": "Smarthome" + // } + //{ + // "Command": 10, + // "Value": "heat", + // "DisplayName": "Heizen", + // "Parameters": [ + // "heat" + // ] + //} + ] + }, + "TabInfoId": 0, + "SpecialType": "none", + "Expanded": true, + "blurryCard": true + }, + { + "Name": "current_target", + "Order": 0, + "RowNr": 10, + "DisplayName": "Aktuelles Ziel: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none", + "Expanded": true, + "BlurryCard": true + }, + { + "Name": "temperature", + "Order": 0, + "RowNr": 20, + "DisplayName": "Ausgelesene Temparatur: ", + "UnitOfMeasurement": " °C", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none", + "BlurryCard": true, + "Expanded": true + }, + { + "Name": "linkQuality", + "Order": 0, + "RowNr": 150, + "DisplayName": "Verbindungsqualität: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none", + "ShowOnlyInDeveloperMode": true, + "BlurryCard": true, + "Expanded": true, + "Precision":0 + }, + { + "Name": "update", + "Order": 0, + "RowNr": 70, + "DisplayName": "Installierte Version: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none", + "ShowOnlyInDeveloperMode": true, + "Expanded": true, + "JsonPath": "$.installed_version", + "BlurryCard": true + }, + { + "Name": "update", + "Order": 0, + "RowNr": 80, + "DisplayName": "Letzte Version: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none", + "ShowOnlyInDeveloperMode": true, + "Expanded": true, + "JsonPath": "$.latest_version", + "BlurryCard": true + }, + //{ + // "Name": "temperature", + // "Order": 2, + // "RowNr": 9, + // "UnitOfMeasurement": " °C", + // "EditParameter": [], + // "EditInfo": { + // "EditType": "Radial", + // "MessageType": "Update", + // "CurrentValueProp": "heating_setpoint", + // "Interval": 5, + // "RadiusFactor": 10.9, + // "ShowValueAbove": true, + // "StartAngle": 265, + // "EndAngle": 275, + // "TickOffset": 0.001, + // "CenterY": 5.45, + // "CenterX": 0.5, + // "LabelOffset": 0.005, + // "Thickness": 0.005, + // "Margin": 0, + // "MinorTickInterval": 5, + // "HeightFactor":0.2, + // "EditParameter": [ + // { + // "Command": 6 + // } + // ] + // }, + // "TabInfoId": 0, + // "Expanded": true, + // "SpecialType": "none" + //}, + // { + // "Name": "heating_setpoint", + // "Order": 0, + // "RowNr": 16, + // "DisplayName": "Manuelle Temperatur Einstellung: ", + // "TextStyle": { + // "FontSize": 16.0, + // "FontFamily": "FontName", + // "FontWeight": "normal", + // "FontStyle": "normal" + // }, + // "TabInfoId": 0, + // "SpecialType": "none" + // }, + { + "Name": "heating_setpoint", + "Order": 1, + "RowNr": 40, + "DisplayName": "Manuelle Temperatur Einstellung: ", + "EditInfo": { + "EditType": "AdvancedSlider", + "MessageType": "Update", + "Display": "", + "EditParameter": [ + { + "Command": 6, + "Digits": 1, + "Value": { + "Min": 5.0, + "Max": 35.0, + "Divisions": 30 + } + } + ], + "GradientColors": [ + [ + 255, + 33, + 150, + 255 + ], + [ + 255, + 255, + 193, + 7 + ], + [ + 255, + 255, + 67, + 54 + ] + ], + "Interval": 5, + "MinorTickInterval": 4 + }, + "Precision": 1, + "TabInfoId": 0, + "SpecialType": "none", + "UnitOfMeasurement": " °C", + "Expanded": true, + "BlurryCard": true + }, + { + "Name": "lastReceivedFormatted", + "Order": 0, + "RowNr": 30, + "DisplayName": "Zuletzt empfangens: ", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "TabInfoId": 0, + "SpecialType": "none", + "Format": "dd", + "BlurryCard": true, + "Expanded": true + } + ], + "TabInfos": [ + { + "Id": 0, + "IconName": "home", + "Order": 1, + "LinkedDevice": null + } + ] + }, + "NotificationSetup": [ + { + "UniqueName": "MyUniqueHeaterNotification", + "TranslatableName": "Einmalig über 21°C", + "Times": "1" + }, + { + "UniqueName": "MyUniqueHeaterNotification2", + "TranslatableName": "Wir deaktiviert" + } + ] +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/TempHumiditySensor.json b/AppBrokerASP/DeviceLayouts/TempHumiditySensor.json new file mode 100644 index 0000000..6ba6b25 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/TempHumiditySensor.json @@ -0,0 +1,118 @@ +{ + "Version": 4, + "UniqueName": "TempHumidityLayout", + "IconName": "XiaomiTempSensor", + "TypeNames": [ + "TH02Z", + "ZG-227Z" + ], + "DashboardDeviceLayout": { + "DashboardProperties": [ + { + "@ref": "$['Base.json'].dashboard.temperature" + }, + { + "@ref": "$['Base.json'].dashboard.humidity" + }, + { + "@ref": "$['Base.json'].dashboard.battery" + }, + // { + // "Name": "isConnected", + // "Order": 1, + // "EditInfo": { + // "EditType": "iconButton", + // "MessageType": "Update", + // "EditParameter": [ + // { + // "Command": "None", + // "Value": true, + // "CodePoint": 59411, + // "Parameters": [ + // false + // ] + // }, + // { + // "Command": "None", + // "Value": false, + // "CodePoint": 59411, + // "FontFamily": "Smarthome", + // "Parameters": [ + // true + // ] + // } + // ] + // }, + // "SpecialType": "right", + // "ShowOnlyInDeveloperMode": true + // } + // { + // "Name": "temperature", + // "Order": 0, + // "TextStyle": { + // "FontSize": 25.0, + // "FontWeight": "bold" + // }, + // "RowNr": 0, + // "UnitOfMeasurement": " °C" + // }, + // { + // "Name": "humidity", + // "Order": 1, + // "RowNr": 1, + // "TextStyle": { + // "FontSize": 15.0, + // "FontWeight": "normal" + // }, + // "UnitOfMeasurement": " %" + // }, + { + "@ref": "$['Base.json'].dashboard.id" + } + ] + }, + "DetailDeviceLayout": { + "PropertyInfos": [ + { + "@ref": "$['Base.json'].detail.friendly_name" + }, + { + "@ref": "$['Base.json'].detail.id" + }, + { + "@ref": "$['Base.json'].detail.last_received" + }, + { + "@ref": "$['Base.json'].detail.link_quality" + }, + { + "@ref": "$['Base.json'].detail.temperature" + }, + { + "@ref": "$['Base.json'].detail.humidity" + }, + { + "@ref": "$['Base.json'].detail.battery" + }, + { + "@ref": "$['Base.json'].detail.milli_voltage" + } + ], + "TabInfos": [ + { + "Id": 0, + "IconName": "home", + "Order": 1, + "LinkedDevice": null + } + ], + "HistoryProperties": [ + { + "@ref": "$['HistoryBase.json'].temperature" + }, + { + "@ref": "$['HistoryBase.json'].humidity" + } + ] + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/TuyaBase.json b/AppBrokerASP/DeviceLayouts/TuyaBase.json new file mode 100644 index 0000000..8df20fa --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/TuyaBase.json @@ -0,0 +1,141 @@ +{ + "dashboard": {}, + "detail": { + "color_temp_label": { + "Name": "color_temp", + "RowNr": 30, + "Order": 0, + "DisplayName": "Farbtemperatur: ", + "UnitOfMeasurement": "", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none" + }, + "color_temp": { + "Name": "color_temp", + "RowNr": 30, + "Order": 0, + "Expanded": true, + "EditInfo": { + "EditType": "slider", + "MessageType": "Update", + "EditParameter": [ + { + "Command": 151, + "Parameters": [ + "color_temp" + ], + "Value": { + "Min": 50.0, + "Max": 1000.0, + "Divisions": 950 + } + } + ], + "Display": "Farbtemperatur", + "ActiveValue": true, + "GradientColors": [ + [ + 255, + 255, + 255, + 255 + ], + [ + 255, + 230, + 160, + 128 + ] + ] + }, + "Precision": 0 + }, + "brigtness_label": { + "Name": "brightness", + "RowNr": 50, + "Order": 0, + "DisplayName": "Helligkeit: ", + "UnitOfMeasurement": "", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none" + }, + "brightness": { + "Name": "brightness", + "RowNr": 50, + "Order": 0, + "Expanded": true, + "EditInfo": { + "EditType": "slider", + "MessageType": "Update", + "EditParameter": [ + { + "Command": 151, + "Parameters": [ + "brightness" + ], + "Value": { + "Min": 0.0, + "Max": 254.0, + "Divisions": 254 + } + } + ], + "Display": "Helligkeit", + "ActiveValue": true, + "GradientColors": [ + [ + 255, + 60, + 60, + 60 + ], + [ + 255, + 255, + 255, + 255 + ] + ] + }, + "Precision": 0 + }, + "light_state_fab": { + "Name": "state", + "RowNr": 100, + "EditInfo": { + "EditType": "FloatingActionButton", + "MessageType": "Update", + "EditParameter": [ + { + "CodePoint": 58595, + "Command": 150, + "Value": true, + "Parameters": [ + "Light.state", + "OFF" + ] + }, + { + "Command": 150, + "CodePoint": 58595, + "Value": false, + "Parameters": [ + "Light.state", + "ON" + ] + } + ] + } + } + } +} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/TuyaSwitchSensor.json b/AppBrokerASP/DeviceLayouts/TuyaSwitchSensor.json index ee35dad..0784f21 100644 --- a/AppBrokerASP/DeviceLayouts/TuyaSwitchSensor.json +++ b/AppBrokerASP/DeviceLayouts/TuyaSwitchSensor.json @@ -1,6 +1,7 @@ { + "Version": 1, "UniqueName": "TuyaLayout", - "IconName": "XiaomiTempSensor", + "IconName": "TuyaSwitchSensor", "TypeNames": [ "TuyaSwitchSensor", "TS011F_plug_1" @@ -8,208 +9,53 @@ "DashboardDeviceLayout": { "DashboardProperties": [ { - "Name": "loadPower", - "Order": 0, - "RowNr": 1, - "TextStyle": { - "FontSize": 20.0, - "FontWeight": "bold" - }, - "UnitOfMeasurement": " W", - "Precision": 0 + "@ref": "$['Base.json'].dashboard.id" }, { - "Name": "current", - "Order": 1, - "RowNr": 30, - "TextStyle": { - "FontSize": 16.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " A", - "Precision": 2 + "@ref": "$['Base.json'].dashboard.last_received" }, { - "Name": "voltage", - "Order": 2, - "RowNr": 30, - "TextStyle": { - "FontSize": 16.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " V", - "Precision": 0 + "@ref": "$['Base.json'].dashboard.link_quality" + }, + { + "@ref": "$['Base.json'].dashboard.load_power" }, { - "Name": "id", - "Order": 2, - "RowNr": 40, - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].dashboard.current" }, { - "Name": "state", - "Order": 2, - "RowNr": 39, - "EditInfo": { - "EditType": "toggle", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "On", - "Value": true - }, - { - "Command": "Off", - "Value": false - } - ], - "ActiveValue": true, - "Display": "Status" - } + "@ref": "$['Base.json'].dashboard.voltage" + }, + { + "@ref": "$['ZigbeeBase.json'].dashboard.toggle" } ] }, "DetailDeviceLayout": { "PropertyInfos": [ { - "Name": "friendlyName", - "Order": 0, - "RowNr": 10, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "id", - "Order": 1, - "RowNr": 11, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true - }, - { - "Name": "loadPower", - "Order": 0, - "RowNr": 13, - "DisplayName": "Verbrauch: ", - "UnitOfMeasurement": " W", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none", - "Precision": 0 + "@ref": "$['Base.json'].detail.friendly_name" }, { - "Name": "current", - "Order": 0, - "RowNr": 14, - "DisplayName": "Stromstärke: ", - "UnitOfMeasurement": " A", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none", - "Precision": 2 + "@ref": "$['Base.json'].detail.id" }, { - "Name": "voltage", - "Order": 0, - "RowNr": 15, - "DisplayName": "Spannung: ", - "UnitOfMeasurement": " V", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none", - "Precision": 0 + "@ref": "$['Base.json'].detail.last_received" }, { - "Name": "energy", - "Order": 0, - "RowNr": 16, - "DisplayName": "Historischer Verbrauch: ", - "UnitOfMeasurement": " kWh", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.link_quality" }, { - "Name": "lastReceivedFormatted", - "Order": 0, - "RowNr": 17, - "DisplayName": "Zuletzt empfangen: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none", - "Format": "dd" + "@ref": "$['Base.json'].detail.load_power" }, { - "Name": "linkQuality", - "Order": 0, - "RowNr": 18, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.current" }, { - "Name": "_", - "Order": 0, - "RowNr": 33, - "DisplayName": "Status: " + "@ref": "$['Base.json'].detail.voltage" }, - { - "Name": "state", - "Order": 1, - "RowNr": 33, - "EditInfo": { - "EditType": "Toggle", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "Off", - "Value": false - }, - { - "Command": "On", - "Value": true - } - ], - "ActiveValue": true - } + "@ref": "$['Base.json'].detail.energy" } ], "HistoryProperties": [ @@ -220,7 +66,7 @@ "IconName": "XiaomiTempSensor", "BrightThemeColor": 4292149248, "DarkThemeColor": 4294922834, - "ChartType": "line" + "ChartType": "step" }, { "PropertyName": "current", @@ -229,7 +75,7 @@ "IconName": "cloud", "BrightThemeColor": 4280902399, "DarkThemeColor": 4286755327, - "ChartType": "line" + "ChartType": "step" }, { "PropertyName": "energy", @@ -247,16 +93,16 @@ "IconName": "flash-59419", "BrightThemeColor": 4278241363, "DarkThemeColor": 4278249078, - "ChartType": "line" + "ChartType": "step" }, { "PropertyName": "state", "XAxisName": "Status", "UnitOfMeasurement": " ", - "IconName": "flash-59419", + "IconName": "celcius-59424", "BrightThemeColor": 4278241363, "DarkThemeColor": 4278249078, - "ChartType": "line" + "ChartType": "step" } ] } diff --git a/AppBrokerASP/DeviceLayouts/XiaomiTempSensor - Sascha.json b/AppBrokerASP/DeviceLayouts/XiaomiTempSensor - Sascha.json deleted file mode 100644 index 9792f2c..0000000 --- a/AppBrokerASP/DeviceLayouts/XiaomiTempSensor - Sascha.json +++ /dev/null @@ -1,316 +0,0 @@ -{ - "UniqueName": "SaschaTempSensorLayout", - "IconName": "XiaomiTempSensor", - "Ids": [ 6066005697233659 ], - "ShowOnlyInDeveloperMode": false, - "DashboardDeviceLayout": { - "DashboardProperties": [ - { - "Name": "temperature", - "Order": 0, - "TextStyle": { - "FontSize": 22.0, - "FontWeight": "bold" - }, - "RowNr": 0, - "UnitOfMeasurement": " °C" - }, - { - "Name": "humidity", - "Order": 1, - "RowNr": 1, - "TextStyle": { - "FontSize": 14.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " %" - }, - { - "Name": "pressure", - "Order": 2, - "RowNr": 1, - "TextStyle": { - "FontSize": 14.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " kpa" - }, - { - "Name": "id", - "Order": 2, - "RowNr": 2, - "ShowOnlyInDeveloperMode": true - }, - { - "Name": "battery", - "Order": 3, - "RowNr": 4, - "EditInfo": { - "EditType": "slider", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "None", - "Parameters": [ "Current" ], - "Value": { - "Min": 0.0, - "Max": 100.0, - "Divisions": 100 - - } - } - ], - "Display": "Battery22", - "GradientColors": [ - - [ 255, 0, 0, 255 ], - [ 255, 0, 255, 0 ], - [ 255, 255, 0, 0 ], - [ 255, 66, 66, 66 ], - - 4294967296 - ] - } - }, - { - "Name": "battery", - "Order": 0, - "EditInfo": { - "EditType": "icon", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "222", - "Value": 0, - "Min": 0, - "Max": 20, - "CodePoint": 59412, - "FontFamily": "Smarthome" - }, - { - "Command": "222", - "Value": 0, - "Min": 20, - "Max": 40, - "CodePoint": 59408, - "FontFamily": "Smarthome" - }, - { - "Command": "222", - "Value": 0, - "Min": 40, - "Max": 60, - "CodePoint": 59409, - "FontFamily": "Smarthome" - }, - { - "Command": "222", - "Value": 0, - "Min": 60, - "Max": 80, - "CodePoint": 59410, - "FontFamily": "Smarthome" - }, - { - "Command": "222", - "Value": 100, - "Min": 80, - "Max": 101, - "CodePoint": 59411, - "FontFamily": "Smarthome" - } - ] - }, - "SpecialType": "right" - }, - { - "Name": "isConnected", - "Order": 1, - "EditInfo": { - "EditType": "iconButton", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "231", - "Value": true, - "CodePoint": 59411, - "Parameters": [ false ] - }, - - { - "Command": "231", - "Value": false, - "CodePoint": 59411, - "FontFamily": "Smarthome", - "Parameters": [ true ] - } - ] - }, - "SpecialType": "right" - } - ] - }, - "DetailDeviceLayout": { - "PropertyInfos": [ - { - "Name": "friendlyName", - "Order": 0, - "RowNr": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "id", - "Order": 1, - "RowNr": 1, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true - }, - { - "Name": "temperature", - "Order": 0, - "RowNr": 2, - "DisplayName": "Temperature: ", - "UnitOfMeasurement": " °C", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "humidity", - "Order": 0, - "RowNr": 3, - "DisplayName": "Luftfeuchtigkeit: ", - "UnitOfMeasurement": " %", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "pressure", - "Order": 0, - "RowNr": 4, - "DisplayName": "Luftdruck: ", - "UnitOfMeasurement": " kPA", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "battery", - "Order": 0, - "RowNr": 5, - "DisplayName": "Battery: ", - "UnitOfMeasurement": " %", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - }, - { - "Name": "lastReceivedFormatted", - "Order": 0, - "RowNr": 6, - "DisplayName": "Zuletzt empfangen: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "Format": "dd" - }, - { - "Name": "link_Quality", - "Order": 0, - "RowNr": 7, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" - } - ], - "TabInfos": [ - { - "Id": 0, - "IconName": "home", - "Order": 1, - "LinkedDevice": null - }, - { - "Id": 1, - "IconName": "off-59399", - "Order": 2, - "LinkedDevice": null - } - ], - "HistoryProperties": [ - { - "PropertyName": "temperature", - "XAxisName": "Temperatur", - "UnitOfMeasurement": " °C", - "IconName": "XiaomiTempSensor", - "BrightThemeColor": 4292149248, - "DarkThemeColor": 4294922834, - "ChartType": "line" - }, - { - "PropertyName": "humidity", - "XAxisName": "rel. Luftfeuchtigkeit", - "UnitOfMeasurement": " %", - "IconName": "cloud", - "BrightThemeColor": 4280902399, - "DarkThemeColor": 4286755327, - "ChartType": "line" - }, - { - "PropertyName": "pressure", - "XAxisName": "Luftdruck", - "UnitOfMeasurement": " kPA", - "IconName": "barometer", - "BrightThemeColor": 4278241363, - "DarkThemeColor": 4278249078, - "ChartType": "line" - } - ] - } -} \ No newline at end of file diff --git a/AppBrokerASP/DeviceLayouts/XiaomiTempSensor.json b/AppBrokerASP/DeviceLayouts/XiaomiTempSensor.json index 51158b4..061cdf9 100644 --- a/AppBrokerASP/DeviceLayouts/XiaomiTempSensor.json +++ b/AppBrokerASP/DeviceLayouts/XiaomiTempSensor.json @@ -1,118 +1,52 @@ { - "Version": 1, + "Version": 2, "UniqueName": "TempSensorLayout", "IconName": "XiaomiTempSensor", - "TypeNames": [ "XiaomiTempSensor", "lumi.weather", "WSDCGQ11LM" ], + "TypeNames": [ + "XiaomiTempSensor", + "lumi.weather", + "WSDCGQ11LM" + ], "DashboardDeviceLayout": { "DashboardProperties": [ { - "Name": "temperature", - "Order": 0, - "TextStyle": { - "FontSize": 21.0, - "FontWeight": "bold" - }, - "RowNr": 0, - "UnitOfMeasurement": " °C" + "@ref": "$['Base.json'].dashboard.temperature" }, { - "Name": "humidity", - "Order": 1, - "RowNr": 1, - "TextStyle": { - "FontSize": 14.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " %" + "@ref": "$['Base.json'].dashboard.humidity" }, { - "Name": "pressure", - "Order": 2, - "RowNr": 1, - "TextStyle": { - "FontSize": 14.0, - "FontWeight": "normal" - }, - "UnitOfMeasurement": " kpa" + "@ref": "$['Base.json'].dashboard.pressure" }, { - "Name": "id", - "Order": 2, - "RowNr": 2, - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].dashboard.id" }, - { - "Name": "battery", - "Order": 0, - "EditInfo": { - "EditType": "icon", - "EditCommand": "Update", - "EditParameter": [ - { - "Command": "None", - "Value": 0, - "Min": 0, - "Max": 20, - "CodePoint": 59412, - "FontFamily": "Smarthome" - }, - { - "Command": "None", - "Value": 0, - "Min": 20, - "Max": 40, - "CodePoint": 59408, - "FontFamily": "Smarthome" - }, - { - "Command": "None", - "Value": 0, - "Min": 40, - "Max": 60, - "CodePoint": 59409, - "FontFamily": "Smarthome" - }, - { - "Command": "None", - "Value": 0, - "Min": 60, - "Max": 80, - "CodePoint": 59410, - "FontFamily": "Smarthome" - }, - { - "Command": "None", - "Value": 100, - "Min": 80, - "Max": 101, - "CodePoint": 59411, - "FontFamily": "Smarthome" - } - ] - }, - "SpecialType": "right" + "@ref": "$['Base.json'].dashboard.battery" }, { "Name": "isConnected", "Order": 1, "EditInfo": { "EditType": "iconButton", - "EditCommand": "Update", + "MessageType": "Update", "EditParameter": [ { "Command": "None", "Value": true, "CodePoint": 59411, - "Parameters": [ false ] + "Parameters": [ + false + ] }, - { "Command": "None", "Value": false, "CodePoint": 59411, "FontFamily": "Smarthome", - "Parameters": [ true ] + "Parameters": [ + true + ] } ] }, @@ -124,134 +58,31 @@ "DetailDeviceLayout": { "PropertyInfos": [ { - "Name": "friendlyName", - "Order": 0, - "RowNr": 0, - "TextStyle": { - "FontSize": 25.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.friendly_name" }, { - "Name": "id", - "Order": 1, - "RowNr": 1, - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "bold", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "ShowOnlyInDeveloperMode": true + "@ref": "$['Base.json'].detail.id" }, { - "Name": "temperature", - "Order": 0, - "RowNr": 2, - "DisplayName": "Temperature: ", - "UnitOfMeasurement": " °C", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.temperature" }, { - "Name": "humidity", - "Order": 0, - "RowNr": 3, - "DisplayName": "Luftfeuchtigkeit: ", - "UnitOfMeasurement": " %", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.humidity" }, { - "Name": "pressure", - "Order": 0, - "RowNr": 4, - "DisplayName": "Luftdruck: ", - "UnitOfMeasurement": " kPA", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.pressure" }, { - "Name": "battery", - "Order": 0, - "RowNr": 5, - "DisplayName": "Battery: ", - "UnitOfMeasurement": " %", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.battery" }, { - "Name": "voltage", - "Order": 0, - "DisplayName": "Spannung: ", - "UnitOfMeasurement": " mV", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.voltage" }, { - "Name": "lastReceivedFormatted", - "Order": 0, - "RowNr": 6, - "DisplayName": "Zuletzt empfangen: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none", - "Format": "dd" + "@ref": "$['Base.json'].detail.last_received" }, { - "Name": "linkQuality", - "Order": 0, - "RowNr": 7, - "DisplayName": "Verbindungsqualität: ", - "TextStyle": { - "FontSize": 16.0, - "FontFamily": "FontName", - "FontWeight": "normal", - "FontStyle": "normal" - }, - "TabInfoId": 0, - "SpecialType": "none" + "@ref": "$['Base.json'].detail.link_quality" } ], "TabInfos": [ @@ -264,31 +95,13 @@ ], "HistoryProperties": [ { - "PropertyName": "temperature", - "XAxisName": "Temperatur", - "UnitOfMeasurement": " °C", - "IconName": "XiaomiTempSensor", - "BrightThemeColor": 4292149248, - "DarkThemeColor": 4294922834, - "ChartType": "line" + "@ref": "$['HistoryBase.json'].temperature" }, { - "PropertyName": "humidity", - "XAxisName": "rel. Luftfeuchtigkeit", - "UnitOfMeasurement": " %", - "IconName": "cloud", - "BrightThemeColor": 4280902399, - "DarkThemeColor": 4286755327, - "ChartType": "line" + "@ref": "$['HistoryBase.json'].humidity" }, { - "PropertyName": "pressure", - "XAxisName": "Luftdruck", - "UnitOfMeasurement": " kPA", - "IconName": "barometer", - "BrightThemeColor": 4278241363, - "DarkThemeColor": 4278249078, - "ChartType": "line" + "@ref": "$['HistoryBase.json'].pressure" } ] } diff --git a/AppBrokerASP/DeviceLayouts/ZigbeeBase.json b/AppBrokerASP/DeviceLayouts/ZigbeeBase.json new file mode 100644 index 0000000..a3ac760 --- /dev/null +++ b/AppBrokerASP/DeviceLayouts/ZigbeeBase.json @@ -0,0 +1,225 @@ +{ + "dashboard": { + "light": { + "Name": "state", + "Order": 2, + "RowNr": 10, + "EditInfo": { + "EditType": "toggle", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "zigbee", + "Value": false, + "Parameters": [ + "Light.state", + "ON" + ] + }, + { + "Command": "zigbee", + "Value": true, + "Parameters": [ + "Light.state", + "OFF" + ] + } + ], + "Display": "Status", + "ActiveValue": true + } + }, + "toggle": { + "Name": "state", + "Order": 1, + "RowNr": 10, + "EditInfo": { + "EditType": "Toggle", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "Off", + "Value": true + }, + { + "Command": "On", + "Value": false + } + ], + "ActiveValue": true + } + } + }, + "detail": { + "light_state": { + "Name": "state", + "RowNr": 20, + "DisplayName": "Status: ", + "UnitOfMeasurement": "", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none" + }, + "color_temp_label": { + "Name": "color_temp", + "RowNr": 30, + "Order": 0, + "DisplayName": "Farbtemperatur: ", + "UnitOfMeasurement": "", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none" + }, + "color_temp": { + "Name": "color_temp", + "RowNr": 30, + "Order": 0, + "Expanded": true, + "EditInfo": { + "EditType": "slider", + "MessageType": "Update", + "EditParameter": [ + { + "Command": 151, + "Parameters": [ + "color_temp" + ], + "Value": { + "Min": 250.0, + "Max": 454.0, + "Divisions": 204 + } + } + ], + "Display": "Farbtemperatur", + "ActiveValue": true, + "GradientColors": [ + [ + 255, + 255, + 255, + 255 + ], + [ + 255, + 230, + 160, + 128 + ] + ] + }, + "Precision": 0 + }, + "brightness_label": { + "Name": "brightness", + "RowNr": 50, + "Order": 0, + "DisplayName": "Helligkeit: ", + "UnitOfMeasurement": "", + "TextStyle": { + "FontSize": 16.0, + "FontFamily": "FontName", + "FontWeight": "normal", + "FontStyle": "normal" + }, + "SpecialType": "none" + }, + "brightness": { + "Name": "brightness", + "RowNr": 50, + "Order": 0, + "Expanded": true, + "EditInfo": { + "EditType": "slider", + "MessageType": "Update", + "EditParameter": [ + { + "Command": 151, + "Parameters": [ + "brightness" + ], + "Value": { + "Min": 0.0, + "Max": 254.0, + "Divisions": 254 + } + } + ], + "Display": "Helligkeit", + "ActiveValue": true, + "GradientColors": [ + [ + 255, + 60, + 60, + 60 + ], + [ + 255, + 255, + 255, + 255 + ] + ] + }, + "Precision": 0 + }, + "light_state_fab": { + "Name": "state", + "RowNr": 100, + "EditInfo": { + "EditType": "FloatingActionButton", + "MessageType": "Update", + "EditParameter": [ + { + "CodePoint": 58595, + "Command": 150, + "Value": true, + "Parameters": [ + "Light.state", + "OFF" + ] + }, + { + "Command": 150, + "CodePoint": 58595, + "Value": false, + "Parameters": [ + "Light.state", + "ON" + ] + } + ] + } + }, + "toggle": { + "Name": "state", + "Order": 1, + "DisplayName": "Status: ", + "RowNr": 40, + "EditInfo": { + "EditType": "Toggle", + "MessageType": "Update", + "EditParameter": [ + { + "Command": "Off", + "Value": true + }, + { + "Command": "On", + "Value": false + } + ], + "ActiveValue": true + } + } + } +} \ No newline at end of file diff --git a/AppBrokerASP/Plugins/IRuntimePlugin.cs b/AppBrokerASP/Plugins/IRuntimePlugin.cs new file mode 100644 index 0000000..74db98f --- /dev/null +++ b/AppBrokerASP/Plugins/IRuntimePlugin.cs @@ -0,0 +1,9 @@ +using AppBroker.Core.Extension; + +namespace AppBrokerASP.Plugins; + +internal interface IRuntimePlugin : IPlugin +{ + void InitializeStartup(string[] args); + void Run(); +} diff --git a/AppBrokerASP/Plugins/PluginLoader.cs b/AppBrokerASP/Plugins/PluginLoader.cs index 99d05b5..2e9eacf 100644 --- a/AppBrokerASP/Plugins/PluginLoader.cs +++ b/AppBrokerASP/Plugins/PluginLoader.cs @@ -20,18 +20,21 @@ namespace AppBrokerASP.Plugins; public class PluginLoader { + public static PluginLoader Instance { get; private set; } + internal List ControllerTypes { get; } = new List(); internal List AppConfigurators { get; } = new(); internal List ServiceExtenders { get; } = new(); internal List Configs { get; } = new(); internal List DeviceTypes { get; } = new(); - private readonly List plugins = new(); + internal List Plugins { get; } = new(); private readonly ILogger logger; public PluginLoader(LogFactory logFactory) { logger = logFactory.GetCurrentClassLogger(); + Instance = this; } internal void LoadPlugins(Assembly ass) @@ -45,7 +48,7 @@ internal void LoadPlugins(Assembly ass) if (typeof(IPlugin).IsAssignableFrom(type)) { logger.Info($"Loading Plugin {type.Name} from Assembly {ass.FullName}"); - plugins.Add(PluginCreator.GetInstance(type)); + Plugins.Add(PluginCreator.GetInstance(type)); } else if (typeof(Device).IsAssignableFrom(type)) { @@ -126,10 +129,10 @@ public void LoadAssemblies() public void InitializePlugins(LogFactory logFactory) { - foreach (IPlugin plugin in plugins.OrderBy(x=>x.LoadOrder)) + foreach (IPlugin plugin in Plugins.OrderBy(x=>x.LoadOrder)) plugin.RegisterTypes(); - foreach (IPlugin plugin in plugins.OrderBy(x => x.LoadOrder)) + foreach (IPlugin plugin in Plugins.OrderBy(x => x.LoadOrder)) { if (!plugin.Initialize(logFactory)) logger.Warn($"Plugin {plugin.Name} had errors in initialization :("); diff --git a/AppBrokerASP/Program.cs b/AppBrokerASP/Program.cs index 1e88f5e..961d6a1 100644 --- a/AppBrokerASP/Program.cs +++ b/AppBrokerASP/Program.cs @@ -1,42 +1,11 @@ -using AppBroker.Core.DynamicUI; - -using Makaretu.Dns; - -using MQTTnet.AspNetCore; - -using NLog; -using NLog.Extensions.Logging; -using NLog.Web; - -using System.Net; -using System.Net.Sockets; +using NLog; using System.Text; -using System.Globalization; using AppBrokerASP.Plugins; -using AppBroker.Core; -using MQTTnet.Server; -using MQTTnet; -using Newtonsoft.Json; -using System.Reflection.Emit; -using System.Reflection; -using Microsoft.AspNetCore.SignalR; -using System.Runtime.InteropServices; -using Microsoft.AspNetCore.Builder; namespace AppBrokerASP; public class Program { -#if DEBUG - public const bool IsDebug = true; - private const int port = 5056; -#else - public const bool IsDebug = false; - private const int port = 5055; -#endif - public static ushort UsedPortForSignalR { get; private set; } - - public static void Main(string[] args) { @@ -45,279 +14,11 @@ public static void Main(string[] args) var pluginLoader = new PluginLoader(LogManager.LogFactory); pluginLoader.LoadAssemblies(); - _ = new InstanceContainer(pluginLoader); - - _ = DeviceLayoutService.InstanceDeviceLayouts; - - Logger? mainLogger = LogManager - .Setup() - .LoadConfigurationFromSection(InstanceContainer.Instance.ConfigManager.Configuration) - .GetCurrentClassLogger(); - - try - { - - UsedPortForSignalR = port; - if (InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenPort == 0) - mainLogger.Info($"ListenPort is not configured in the appsettings serverconfig section and therefore default port {port} will be used, when no port was passed into listen url."); - else - UsedPortForSignalR = InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenPort; - - string[] listenUrls; - if (InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls.Any()) - { - listenUrls = new string[InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls.Count]; - for (int i = 0; i < InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls.Count; i++) - { - string? item = InstanceContainer.Instance.ServerConfigManager.ServerConfig.ListenUrls[i]; - try - { - var builder = new UriBuilder(item) - { - //TODO only override default ports? How do we advertise multiple ports or will there be a main port, multiple advertisments or what is the plan? - //if ((builder.Scheme == "http" && builder.Port == 80) || (builder.Scheme == "https" && builder.Port == 443)) - //{ - Port = UsedPortForSignalR - }; - //} - listenUrls[i] = builder.ToString(); - } - catch (UriFormatException ex) - { - mainLogger.Error($"Error during process of {item}", ex); - } - } - } - else - { - listenUrls = new[] { $"http://*:{UsedPortForSignalR}" }; - } - AdvertiseServerPortsViaMDNS(UsedPortForSignalR); - - mainLogger.Info($"Listening on urls {string.Join(",", listenUrls)}"); - - WebApplicationBuilder? webBuilder = WebApplication - .CreateBuilder(new WebApplicationOptions() { Args = args, WebRootPath = "wwwroot" }); - - _ = webBuilder.Host.ConfigureAppConfiguration((hostingContext, config) => - { - config.Sources.Clear(); - _ = config.AddConfiguration(InstanceContainer.Instance.ConfigManager.Configuration); - }) - ; - - - _ = webBuilder.WebHost.UseKestrel((ks) => - { - foreach (var url in listenUrls) - { - var uri = new Uri(url); - ks.Listen(CreateIPEndPoint(uri.Host + ":" + uri.Port)); - } - - if (InstanceContainer.Instance.ConfigManager.MqttConfig.Enabled) - { - ks.ListenAnyIP(InstanceContainer.Instance.ConfigManager.MqttConfig.Port, x => x.UseMqtt()); - } - }); - - _ = webBuilder.Host.ConfigureLogging(logging => - { - _ = logging.ClearProviders(); - _ = logging.AddNLog(); - }); - var mqttConfig = InstanceContainer.Instance.ConfigManager.MqttConfig; - - var startup = new Startup(webBuilder.Configuration); - startup.ConfigureServices(webBuilder.Services); - List hubTypes = new(); - foreach (var extender in pluginLoader.ServiceExtenders) - { - extender.ConfigureServices(webBuilder.Services); - foreach (var type in extender.GetHubTypes()) - { - hubTypes.Add(type); - } - } - - WebApplication? app = webBuilder.Build(); - _ = app.UseWebSockets(); - _ = app.UseCors("CorsPolicy"); - _ = app.UseRouting(); - _ = app.UseStaticFiles(); - - Type dynamicHub = GenerateDynamicHub(hubTypes, mainLogger); - _ = app.UseEndpoints(e => - { - //_ = e.MapFallbackToPage("/_Host"); - _ = e.MapControllers(); - var mapHubMethod = typeof(HubEndpointRouteBuilderExtensions).GetMethod("MapHub", 1, new[] { typeof(IEndpointRouteBuilder), typeof(string) }); - _ = mapHubMethod.MakeGenericMethod(dynamicHub).Invoke(null, new object[] { e, "/Smarthome" }); - _ = mapHubMethod.MakeGenericMethod(dynamicHub).Invoke(null, new object[] { e, "/Smarthome/{id}" }); - - foreach (var extender in pluginLoader.ServiceExtenders) - { - extender.UseEndpoints(e); - } - - if (mqttConfig.Enabled) - { - _ = e.MapConnectionHandler( - "/MQTTClient", - httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector = - protocolList => protocolList.FirstOrDefault() ?? string.Empty); - } - }); - - if (mqttConfig.Enabled) - { - _ = app.UseMqttServer(server => - { - static async Task Server_RetainedMessagesClearedAsync(EventArgs arg) => File.Delete(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath); - static Task Server_LoadingRetainedMessageAsync(LoadingRetainedMessagesEventArgs arg) - { - if (File.Exists(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath)) - { - var json = File.ReadAllText(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath); - arg.LoadedRetainedMessages = JsonConvert.DeserializeObject>(json); - } - else - { - arg.LoadedRetainedMessages = new List(); - } - return Task.CompletedTask; - } - - static async Task Server_RetainedMessageChangedAsync(RetainedMessageChangedEventArgs arg) => File.WriteAllText(InstanceContainer.Instance.ConfigManager.MqttConfig.RetainedMessageFilePath, JsonConvert.SerializeObject(arg.StoredRetainedMessages)); - ; - - - server.RetainedMessageChangedAsync += Server_RetainedMessageChangedAsync; - server.LoadingRetainedMessageAsync += Server_LoadingRetainedMessageAsync; - server.RetainedMessagesClearedAsync += Server_RetainedMessagesClearedAsync; - // Todo: Do something with the server - // https://github.com/dotnet/MQTTnet/wiki/Server#aspnet-50= - }); - } - if (InstanceContainer.Instance.ServerConfigManager.ServerConfig.EnableJavaScript) - { - InstanceContainer.Instance.JavaScriptEngineManager.Initialize(); - } - if (app.Environment.IsDevelopment()) - { - //app.UseSwagger(); - //app.UseSwaggerUI(); - app.UseOpenApi(c => - { - }); // serve documents (same as app.UseSwagger()) - //app.UseSwaggerUi3(); // serve Swagger UI - app.UseReDoc(); // serve ReDoc UI - } - pluginLoader.InitializePlugins(LogManager.LogFactory); - - app.Run(); - } - catch (Exception ex) - { - mainLogger.Error(ex, "Stopped program because of exception"); - throw; - } - finally - { - NLog.LogManager.Shutdown(); - } + var runtime = pluginLoader.Plugins.OfType().First(); + runtime.InitializeStartup(args); + pluginLoader.InitializePlugins(LogManager.LogFactory); + runtime.Run(); } - private static IPEndPoint CreateIPEndPoint(string endPoint) - { - string[] ep = endPoint.Split(':'); - if (ep.Length < 2) - throw new FormatException("Invalid endpoint format"); - - IPAddress? ip; - if (ep.Length > 2) - { - if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip)) - throw new FormatException("Invalid ip-adress"); - } - else - { - if (!IPAddress.TryParse(ep[0], out ip)) - throw new FormatException("Invalid ip-adress"); - } - - if (!int.TryParse(ep[^1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out int port)) - throw new FormatException("Invalid port"); - - return new IPEndPoint(ip, port); - } - private static void AdvertiseServerPortsViaMDNS(ushort port) - { - MulticastService mdns = new(); - ServiceDiscovery sd = new(mdns); - - IPAddress[]? hostEntry - = Dns.GetHostEntry(Environment.MachineName) - .AddressList - .Where(x => x.AddressFamily == AddressFamily.InterNetwork - || (x.AddressFamily == AddressFamily.InterNetworkV6 - && !x.IsIPv6SiteLocal - && !x.IsIPv6LinkLocal - && !x.IsIPv6UniqueLocal - && !x.IsIPv6Teredo)) - .ToArray(); - - var serv = new ServiceProfile(InstanceContainer.Instance.ServerConfigManager.ServerConfig.InstanceName, "_smarthome._tcp", port, hostEntry); - - //serv.AddProperty("Min App Version", "0.0.2"); //Currently not needed, but supported by flutter app - if (IsDebug) - serv.AddProperty("Debug", IsDebug.ToString()); - if (string.IsNullOrWhiteSpace(InstanceContainer.Instance.ServerConfigManager.ServerConfig.ClusterId)) - serv.AddProperty("ClusterId", InstanceContainer.Instance.ServerConfigManager.ServerConfig.ClusterId); - sd.Advertise(serv); - - mdns.Start(); - } - - private static Type GenerateDynamicHub(List hubTypes, Logger mainLogger) - { - AssemblyName assemblyName = new AssemblyName("DynamicHubAssembly"); - AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); - ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule"); - TypeBuilder typeBuilder = moduleBuilder.DefineType("RuntimeHub", TypeAttributes.Public, typeof(DynamicHub)); - foreach (var hubType in hubTypes) - { - foreach (var method in hubType.GetMethods()) - { - if (method.DeclaringType != hubType || (method.Attributes & MethodAttributes.Private) > 0) - continue; - if ((method.Attributes & MethodAttributes.Static) == 0) - { - mainLogger.Warn("Method {0}.{1} was not static, only public static methods are supported", hubType.FullName, method.Name); - continue; - } - var parameters = method.GetParameters(); - bool passThis = false; - if (parameters.Length > 0 && parameters[0].ParameterType == typeof(DynamicHub)) - passThis = true; - MethodBuilder methodBuilder = typeBuilder.DefineMethod( - method.Name, - MethodAttributes.Public | MethodAttributes.HideBySig, - method.ReturnType, - parameters.Skip(passThis ? 1 : 0).Select(x => x.ParameterType).ToArray()); - - var gen = methodBuilder.GetILGenerator(); - - for (int i = 0; i < parameters.Length; i++) - { - gen.Emit(OpCodes.Ldarg, i + (passThis ? 0 : 1)); - } - gen.EmitCall(OpCodes.Call, method, null); - gen.Emit(OpCodes.Ret); - } - } - Type dynamicType = typeBuilder.CreateType(); - return dynamicType; - } } diff --git a/AppBrokerASP/Smarthome.cs b/AppBrokerASP/Smarthome.cs deleted file mode 100644 index 7b60143..0000000 --- a/AppBrokerASP/Smarthome.cs +++ /dev/null @@ -1,219 +0,0 @@ -using AppBroker.Core; -using AppBroker.Core.Database; -using AppBroker.Core.Devices; -using AppBroker.Core.DynamicUI; -using AppBroker.Core.Models; - -using AppBrokerASP.Plugins; - -using Microsoft.AspNetCore.SignalR; - -using Newtonsoft.Json; - -using NLog.Targets; - -using System.Reflection; -using System.Threading.Tasks; - -namespace AppBrokerASP; - -//public class SmartHome : Hub -//{ -// public SmartHome() -// { -// } - - - - //public override Task OnConnectedAsync() => - //foreach (var item in IInstanceContainer.Instance.DeviceManager.Devices.Values) - // item.SendLastData(Clients.Caller); - - //base.OnConnectedAsync(); - - //public async Task Update(JsonSmarthomeMessage message) - //{ - // if (IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(message.LongNodeId, out Device? device)) - // { - // switch (message.MessageType) - // { - // case MessageType.Get: - // break; - // case MessageType.Update: - // await device.UpdateFromApp(message.Command, message.Parameters); - // break; - // case MessageType.Options: - // device.OptionsFromApp(message.Command, message.Parameters); - // break; - // default: - // break; - // } - // //Console.WriteLine($"User send command {message.Command} to {device} with {message.Parameters}"); - // } - //} - - //public void UpdateDevice(long id, string newName) - //{ - // if (IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(id, out Device? stored)) - // { - // stored.FriendlyName = newName; - // _ = DbProvider.UpdateDeviceInDb(stored); - // stored.SendDataToAllSubscribers(); - // } - - //} - - //public dynamic? GetConfig(long deviceId) => IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(deviceId, out Device? device) ? device.GetConfig() : null; - - //public async void SendUpdate(Device device) => await (Clients.All?.Update(device) ?? Task.CompletedTask); - - //public List GetAllDevices() - //{ - // var devices = IInstanceContainer.Instance.DeviceManager.Devices.Select(x => x.Value).Where(x => x.ShowInApp).ToList(); - // var dev = JsonConvert.SerializeObject(devices); - - // return devices; - //} - - //public List GetHistoryPropertySettings() => IInstanceContainer.Instance.HistoryManager.GetHistoryProperties(); - //public void SetHistory(bool enable, long id, string name) - //{ - // if (enable) - // IInstanceContainer.Instance.HistoryManager.EnableHistory(id, name); - // else - // IInstanceContainer.Instance.HistoryManager.DisableHistory(id, name); - //} - //public void SetHistories(bool enable, List ids, string name) - //{ - // if (enable) - // { - // foreach (var id in ids) - // IInstanceContainer.Instance.HistoryManager.EnableHistory(id, name); - // } - // else - // { - // foreach (var id in ids) - // IInstanceContainer.Instance.HistoryManager.DisableHistory(id, name); - // } - //} - - //public record struct DeviceOverview(long Id, string TypeName, IReadOnlyCollection TypeNames, string FriendlyName); - //public List GetDeviceOverview() => IInstanceContainer.Instance.DeviceManager.Devices.Select(x => x.Value).Where(x => x.ShowInApp).Select(x => new DeviceOverview(x.Id, x.TypeName, x.TypeNames, x.FriendlyName)).ToList(); - - - //public Task> GetIoBrokerHistories(long id, string dt) - //{ - // if (IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(id, out Device? device)) - // { - // DateTime date = DateTime.Parse(dt).Date; - // return device.GetHistory(date, date.AddDays(1).AddSeconds(-1)); - // } - // return Task.FromResult(new List()); - //} - - //public virtual Task GetIoBrokerHistory(long id, string dt, string propertyName) - //{ - // if (IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(id, out Device? device)) - // { - // DateTime date = DateTime.Parse(dt).Date; - // return device.GetHistory(date, date.AddDays(1).AddSeconds(-1), propertyName); - // } - // return Task.FromResult(History.Empty); - //} - - //public Task> GetIoBrokerHistoriesRange(long id, string dt, string dt2) - //{ - // if (IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(id, out Device? device)) - // { - // return device.GetHistory(DateTime.Parse(dt), DateTime.Parse(dt2)); - // } - - // return Task.FromResult(new List()); - //} - - //public virtual async Task GetIoBrokerHistoryRange(long id, string dt, string dt2, string propertyName) - //{ - // if (IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(id, out Device? device)) - // { - // return await device.GetHistory(DateTime.Parse(dt), DateTime.Parse(dt2), propertyName) - // ; - // } - - // return History.Empty; - //} - - - //public List Subscribe(List DeviceIds) - //{ - // string connectionId = Context.ConnectionId; - // var devices = new List(); - // string? subMessage = "User subscribed to "; - // foreach (long deviceId in DeviceIds) - // { - - // if (!IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(deviceId, out Device? device)) - // continue; - - - // if (!device.Subscribers.Any(x => x.ConnectionId == connectionId)) - // device.Subscribers.Add(new Subscriber(connectionId, Clients.Caller)); - // devices.Add(device); - // subMessage += device.Id + "/" + device.FriendlyName + ", "; - // } - // Console.WriteLine(subMessage); - // var dev = JsonConvert.SerializeObject(devices); - - // return devices; - //} - - //public void Unsubscribe(List DeviceIds) - //{ - // string connectionId = Context.ConnectionId; - // var devices = new List(); - // string? subMessage = "User unsubscribed from "; - // foreach (long deviceId in DeviceIds) - // { - // if (IInstanceContainer.Instance.DeviceManager.Devices.TryGetValue(deviceId, out Device? device)) - // { - - // _ = device.Subscribers.RemoveWhere(x => x.ConnectionId == connectionId); - // subMessage += device.Id + "/" + device.FriendlyName + ", "; - // } - // } - // Console.WriteLine(subMessage); - //} - - //public void UpdateTime() - //{ - // //TODO How to call smarthome mesh manager update time, without knowing the existence of said manager? - //} - - //public string GetHashCodeByTypeName(string typeName) => InstanceContainer.Instance.IconService.GetBestFitIcon(typeName).Hash; - //public string GetHashCodeByName(string iconName) => InstanceContainer.Instance.IconService.GetIconByName(iconName).Hash; - - //public SvgIcon GetIconByTypeName(string typename) => InstanceContainer.Instance.IconService.GetBestFitIcon(typename); - //public SvgIcon GetIconByName(string iconName) => InstanceContainer.Instance.IconService.GetIconByName(iconName); - //public SvgIcon GetIconByDeviceId(long deviceId) => InstanceContainer.Instance.IconService.GetBestFitIcon(InstanceContainer.Instance.DeviceManager.Devices[deviceId].TypeName); - - //public void ReloadDeviceLayouts() => DeviceLayoutService.ReloadLayouts(); - //public DeviceLayout? GetDeviceLayoutByName(string typename) => DeviceLayoutService.GetDeviceLayout(typename)?.layout; - //public DeviceLayout? GetDeviceLayoutByDeviceId(long id) => DeviceLayoutService.GetDeviceLayout(id)?.layout; - //public List GetAllDeviceLayouts() => DeviceLayoutService.GetAllLayouts(); - - //public record LayoutNameWithHash(string Name, string Hash); - //public LayoutNameWithHash? GetDeviceLayoutHashByDeviceId(long id) - //{ - - // var layoutHash = DeviceLayoutService.GetDeviceLayout(id); - // if (layoutHash is null || layoutHash.Value.layout is null) - // return null; - - // return new(layoutHash.Value.layout.UniqueName, layoutHash.Value.hash); - //} - - - //public DashboardDeviceLayout? GetDashboardDeviceLayoutByName(string typename) => DeviceLayoutService.GetDashboardDeviceLayout(typename); - //public DashboardDeviceLayout? GetDashboardDeviceLayoutByDeviceId(long id) => DeviceLayoutService.GetDashboardDeviceLayout(id); - //public DetailDeviceLayout? GetDetailDeviceLayoutByName(string typename) => DeviceLayoutService.GetDetailDeviceLayout(typename); - //public DetailDeviceLayout? GetDetailDeviceLayoutByDeviceId(long id) => DeviceLayoutService.GetDetailDeviceLayout(id); -//}