diff --git a/.linting/roslynator.ruleset b/.linting/roslynator.ruleset
index 967ecd465..52a4ec943 100644
--- a/.linting/roslynator.ruleset
+++ b/.linting/roslynator.ruleset
@@ -25,7 +25,14 @@ Just add ruleset file to a solution and open it.
+
+
+
+
+
+
+
diff --git a/.vscode/daemon.code-snippets b/.vscode/daemon.code-snippets
index 4918506db..f2c7e35cb 100644
--- a/.vscode/daemon.code-snippets
+++ b/.vscode/daemon.code-snippets
@@ -12,6 +12,14 @@
// "description": "Log output to console"
// }
{
+ "nullcheck": {
+ "scope": "csharp",
+ "prefix": "nullcheck",
+ "body": [
+ "_ = $1 ??",
+ " throw new NetDaemonArgumentNullException(nameof($1));"
+ ]
+ },
"fact": {
"scope": "csharp",
"prefix": "fact",
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/CodeManager.cs b/src/Daemon/NetDaemon.Daemon/Daemon/CodeManager.cs
index 1c5139237..4c1615908 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/CodeManager.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/CodeManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -31,6 +32,7 @@ public CodeManager(IEnumerable daemonAppTypes, ILogger logger, IYamlConfig
_yamlConfig = yamlConfig;
}
+ [SuppressMessage("", "CA1065")]
public int Count => _loadedDaemonApps?.Count() ?? throw new NetDaemonNullReferenceException("_loadedDaemonApps cannot be null");
// Internal for testing
@@ -57,7 +59,8 @@ public IEnumerable InstanceDaemonApps()
{
try
{
- var yamlAppConfig = new YamlAppConfig(_loadedDaemonApps, File.OpenText(file), _yamlConfig, file);
+ using var fileReader = File.OpenText(file);
+ var yamlAppConfig = new YamlAppConfig(_loadedDaemonApps, fileReader, _yamlConfig, file);
foreach (var appInstance in yamlAppConfig.Instances)
{
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/Config/ConfigExtensions.cs b/src/Daemon/NetDaemon.Daemon/Daemon/Config/ConfigExtensions.cs
index 9f668ef94..f413e67c6 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/Config/ConfigExtensions.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/Config/ConfigExtensions.cs
@@ -1,7 +1,9 @@
-using System.Reflection;
+using System.Globalization;
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
+using NetDaemon.Common.Exceptions;
[assembly: InternalsVisibleTo("NetDaemon.Daemon.Tests")]
@@ -15,6 +17,9 @@ public static class TaskExtensions
{
public static async Task InvokeAsync(this MethodInfo mi, object? obj, params object?[]? parameters)
{
+ _ = mi ??
+ throw new NetDaemonArgumentNullException(nameof(mi));
+
dynamic? awaitable = mi.Invoke(obj, parameters);
if (awaitable != null)
await awaitable.ConfigureAwait(false);
@@ -25,6 +30,9 @@ public static class ConfigStringExtensions
{
public static string ToPythonStyle(this string str)
{
+ _ = str ??
+ throw new NetDaemonArgumentNullException(nameof(str));
+
var build = new StringBuilder(str.Length);
bool isStart = true;
foreach (char c in str)
@@ -33,13 +41,16 @@ public static string ToPythonStyle(this string str)
build.Append('_');
else
isStart = false;
- build.Append(char.ToLower(c));
+ build.Append(char.ToLower(c, CultureInfo.InvariantCulture));
}
return build.ToString();
}
public static string ToCamelCase(this string str)
{
+ _ = str ??
+ throw new NetDaemonArgumentNullException(nameof(str));
+
var build = new StringBuilder();
bool nextIsUpper = false;
bool isFirstCharacter = true;
@@ -51,7 +62,7 @@ public static string ToCamelCase(this string str)
continue;
}
- build.Append(nextIsUpper || isFirstCharacter ? char.ToUpper(c) : c);
+ build.Append(nextIsUpper || isFirstCharacter ? char.ToUpper(c, CultureInfo.InvariantCulture) : c);
nextIsUpper = false;
isFirstCharacter = false;
}
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlAppConfig.cs b/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlAppConfig.cs
index 8b0c3604b..6cb10e8b2 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlAppConfig.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlAppConfig.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using NetDaemon.Common;
@@ -26,6 +27,8 @@ public YamlAppConfig(IEnumerable types, TextReader reader, IYamlConfig yam
_yamlFilePath = yamlFilePath;
}
+ [SuppressMessage("", "CA1508")]
+ [SuppressMessage("", "CA1065")]
public IEnumerable Instances
{
get
@@ -59,7 +62,7 @@ public IEnumerable Instances
}
}
}
- catch (System.Exception e)
+ catch (Exception e)
{
throw new NetDaemonException($"Error instancing application {appId}", e);
}
@@ -74,10 +77,10 @@ public IEnumerable Instances
YamlMappingNode appNode,
string? appId)
{
- var netDaemonApp = (INetDaemonAppBase?)Activator.CreateInstance(netDaemonAppType);
+ _ = appNode ??
+ throw new NetDaemonArgumentNullException(nameof(appNode));
- if (netDaemonApp == null)
- return null;
+ var netDaemonApp = (INetDaemonAppBase?)Activator.CreateInstance(netDaemonAppType);
foreach (KeyValuePair entry in appNode.Children)
{
@@ -106,7 +109,8 @@ public IEnumerable Instances
return netDaemonApp;
}
- private object? InstanceProperty(Object? parent, Type instanceType, YamlNode node)
+ [SuppressMessage("", "CA1508")] // Weird bug that this should not warn!
+ private object? InstanceProperty(object? parent, Type instanceType, YamlNode node)
{
if (node.NodeType == YamlNodeType.Scalar)
{
@@ -197,7 +201,8 @@ private void ReplaceSecretIfExists(YamlScalarNode scalarNode)
{
return null;
}
- return ((YamlScalarNode)classChild.Value)?.Value?.ToLowerInvariant();
+ var scalarNode = (YamlScalarNode)classChild.Value;
+ return scalarNode.Value?.ToLowerInvariant();
}
}
}
\ No newline at end of file
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlConfig.cs b/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlConfig.cs
index d7965edaf..538137a71 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlConfig.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlConfig.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Options;
@@ -24,7 +25,11 @@ public class YamlConfig : IYamlConfig
public YamlConfig(IOptions netDaemonSettings)
{
+ _ = netDaemonSettings ??
+ throw new NetDaemonArgumentNullException(nameof(netDaemonSettings));
+
_configFolder = netDaemonSettings.Value.GetAppSourceDirectory();
+
_secrets = GetAllSecretsFromPath(_configFolder);
}
@@ -53,9 +58,11 @@ public IEnumerable GetAllConfigFilePaths()
internal static Dictionary GetSecretsFromSecretsYaml(string file)
{
- return GetSecretsFromSecretsYaml(File.OpenText(file));
+ using var fileReader = File.OpenText(file);
+ return GetSecretsFromSecretsYaml(fileReader);
}
+ [SuppressMessage("", "CA1508")] // TODO: Need to refactor this
internal static Dictionary GetSecretsFromSecretsYaml(TextReader reader)
{
var result = new Dictionary();
@@ -94,8 +101,8 @@ public IEnumerable GetAllConfigFilePaths()
if (!result.ContainsKey(fileDirectory))
{
- var secretsFromFile = GetSecretsFromSecretsYaml(file);
- result[fileDirectory] = secretsFromFile;
+ result[fileDirectory] = (Dictionary?)GetSecretsFromSecretsYaml(file) ??
+ new Dictionary();
}
}
return result;
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlExtensions.cs b/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlExtensions.cs
index 9d121c82e..0b13fe40f 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlExtensions.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/Config/YamlExtensions.cs
@@ -4,6 +4,7 @@
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
+using NetDaemon.Common.Exceptions;
using YamlDotNet.RepresentationModel;
[assembly: InternalsVisibleTo("NetDaemon.Daemon.Tests")]
@@ -25,14 +26,24 @@ public static class YamlExtensions
{
public static PropertyInfo? GetYamlProperty(this Type type, string propertyName)
{
+ _ = type ??
+ throw new NetDaemonArgumentNullException(nameof(type));
+
// Lets try convert from python style to CamelCase
+
var prop = type.GetProperty(propertyName) ?? type.GetProperty(propertyName.ToCamelCase());
return prop;
}
public static object? ToObject(this YamlScalarNode node, Type valueType)
{
+ _ = valueType ??
+ throw new NetDaemonArgumentNullException(nameof(valueType));
+ _ = node ??
+ throw new NetDaemonArgumentNullException(nameof(node));
+
Type? underlyingNullableType = Nullable.GetUnderlyingType(valueType);
+
if (underlyingNullableType != null)
{
// It is nullable type
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/DaemonAppExtensions.cs b/src/Daemon/NetDaemon.Daemon/Daemon/DaemonAppExtensions.cs
index 985c9f2e3..3a0c0e36e 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/DaemonAppExtensions.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/DaemonAppExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Linq;
using System.Reflection;
@@ -6,6 +7,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NetDaemon.Common;
+using NetDaemon.Common.Exceptions;
using NetDaemon.Common.Fluent;
using NetDaemon.Common.Reactive;
using NetDaemon.Daemon.Config;
@@ -16,9 +18,15 @@ namespace NetDaemon.Daemon
{
public static class DaemonAppExtensions
{
- public static async Task HandleAttributeInitialization(this INetDaemonAppBase netDaemonApp, INetDaemon _daemon)
+ public static async Task HandleAttributeInitialization(this INetDaemonAppBase netDaemonApp, INetDaemon daemon)
{
+ _ = daemon ??
+ throw new NetDaemonArgumentNullException(nameof(daemon));
+ _ = netDaemonApp ??
+ throw new NetDaemonArgumentNullException(nameof(netDaemonApp));
+
var netDaemonAppType = netDaemonApp.GetType();
+
foreach (var method in netDaemonAppType.GetMethods())
{
foreach (var attr in method.GetCustomAttributes(false))
@@ -28,11 +36,11 @@ public static async Task HandleAttributeInitialization(this INetDaemonAppBase ne
switch (attr)
{
case HomeAssistantServiceCallAttribute:
- await HandleServiceCallAttribute(_daemon, daemonApp, method, true).ConfigureAwait(false);
+ await HandleServiceCallAttribute(daemon, daemonApp, method, true).ConfigureAwait(false);
break;
case HomeAssistantStateChangedAttribute hassStateChangedAttribute:
- HandleStateChangedAttribute(_daemon, hassStateChangedAttribute, daemonApp, method);
+ HandleStateChangedAttribute(daemon, hassStateChangedAttribute, daemonApp, method);
break;
}
}
@@ -41,7 +49,7 @@ public static async Task HandleAttributeInitialization(this INetDaemonAppBase ne
switch (attr)
{
case HomeAssistantServiceCallAttribute:
- await HandleServiceCallAttribute(_daemon, daemonRxApp, method, false).ConfigureAwait(false);
+ await HandleServiceCallAttribute(daemon, daemonRxApp, method, false).ConfigureAwait(false);
break;
}
}
@@ -91,6 +99,7 @@ private static (bool, string) CheckIfStateChangedSignatureIsOk(MethodInfo method
return (true, string.Empty);
}
+ [SuppressMessage("", "CA1031")]
private static async Task HandleServiceCallAttribute(INetDaemon _daemon, NetDaemonAppBase netDaemonApp, MethodInfo method, bool async = true)
{
var (signatureOk, err) = CheckIfServiceCallSignatureIsOk(method, async);
@@ -121,6 +130,7 @@ private static async Task HandleServiceCallAttribute(INetDaemon _daemon, NetDaem
});
}
+ [SuppressMessage("", "CA1031")]
private static void HandleStateChangedAttribute(
INetDaemon _daemon,
HomeAssistantStateChangedAttribute hassStateChangedAttribute,
@@ -141,16 +151,14 @@ MethodInfo method
{
try
{
- if (hassStateChangedAttribute.To != null)
+ if (hassStateChangedAttribute.To != null && (dynamic)hassStateChangedAttribute.To != to?.State)
{
- if ((dynamic)hassStateChangedAttribute.To != to?.State)
- return;
+ return;
}
- if (hassStateChangedAttribute.From != null)
+ if (hassStateChangedAttribute.From != null && (dynamic)hassStateChangedAttribute.From != from?.State)
{
- if ((dynamic)hassStateChangedAttribute.From != from?.State)
- return;
+ return;
}
// If we don´t accept all changes in the state change
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/HttpHandler.cs b/src/Daemon/NetDaemon.Daemon/Daemon/HttpHandler.cs
index 165e665f8..1e0f01ecd 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/HttpHandler.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/HttpHandler.cs
@@ -27,11 +27,11 @@ public HttpClient CreateHttpClient(string? name = null)
{
_ = _httpClientFactory ?? throw new NetDaemonNullReferenceException("No IHttpClientFactory provided, please add AddHttpClient() in configure services!");
- var httpClient = _httpClientFactory.CreateClient();
+ using var httpClient = _httpClientFactory.CreateClient();
AddHeaders(httpClient, headers);
- var streamTask = httpClient.GetStreamAsync(url)
+ var streamTask = httpClient.GetStreamAsync(new Uri(url))
?? throw new NetDaemonException($"Unexpected, nothing returned from {url}");
return await JsonSerializer.DeserializeAsync(await streamTask.ConfigureAwait(false), options).ConfigureAwait(false);
@@ -40,14 +40,16 @@ public HttpClient CreateHttpClient(string? name = null)
public async Task PostJson(string url, object request, JsonSerializerOptions? options = null, params (string, object)[] headers)
{
_ = _httpClientFactory ?? throw new NetDaemonNullReferenceException("No IHttpClientFactory provided, please add AddHttpClient() in configure services!");
+ _ = request ?? throw new NetDaemonArgumentNullException(nameof(request));
- var httpClient = _httpClientFactory.CreateClient();
+ using var httpClient = _httpClientFactory.CreateClient();
AddHeaders(httpClient, headers);
var bytesToPost = JsonSerializer.SerializeToUtf8Bytes(request, request.GetType(), options);
+ using var content = new ByteArrayContent(bytesToPost);
- var response = await httpClient.PostAsync(url, new ByteArrayContent(bytesToPost)).ConfigureAwait(false);
+ var response = await httpClient.PostAsync(new Uri(url), content).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
@@ -58,14 +60,16 @@ public HttpClient CreateHttpClient(string? name = null)
public async Task PostJson(string url, object request, JsonSerializerOptions? options = null, params (string, object)[] headers)
{
_ = _httpClientFactory ?? throw new NetDaemonNullReferenceException("No IHttpClientFactory provided, please add AddHttpClient() in configure services!");
+ _ = request ?? throw new NetDaemonArgumentNullException(nameof(request));
- var httpClient = _httpClientFactory.CreateClient();
+ using var httpClient = _httpClientFactory.CreateClient();
AddHeaders(httpClient, headers);
var bytesToPost = JsonSerializer.SerializeToUtf8Bytes(request, request.GetType(), options);
+ using var content = new ByteArrayContent(bytesToPost);
- var response = await httpClient.PostAsync(url, new ByteArrayContent(bytesToPost)).ConfigureAwait(false);
+ var response = await httpClient.PostAsync(new Uri(url), content).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/NetDaemonHost.cs b/src/Daemon/NetDaemon.Daemon/Daemon/NetDaemonHost.cs
index 2c7d1f162..ef565a1fe 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/NetDaemonHost.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/NetDaemonHost.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
+using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
@@ -128,6 +130,7 @@ public IHttpHandler Http
public IScheduler Scheduler => _scheduler;
+ [SuppressMessage("", "CA1721")]
public IEnumerable State => InternalState.Select(n => n.Value);
// For testing
@@ -144,22 +147,23 @@ public IHttpHandler Http
public async Task> GetAllServices()
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return await _hassClient.GetServices().ConfigureAwait(false);
}
public void CallService(string domain, string service, dynamic? data = null)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
if (!_serviceCallMessageChannel.Writer.TryWrite((domain, service, data)))
throw new NetDaemonException("Servicecall queue full!");
}
+ [SuppressMessage("", "CA1031")]
public async Task CallServiceAsync(string domain, string service, dynamic? data = null, bool waitForResponse = false)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
try
{
@@ -174,21 +178,23 @@ public async Task CallServiceAsync(string domain, string service, dynamic? data
///
public ICamera Camera(INetDaemonApp app, params string[] entityIds)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return new CameraManager(entityIds, this, app);
}
///
public ICamera Cameras(INetDaemonApp app, IEnumerable entityIds)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return new CameraManager(entityIds, this, app);
}
///
public ICamera Cameras(INetDaemonApp app, Func func)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _ = app ??
+ throw new NetDaemonArgumentNullException(nameof(app));
+ _cancelToken.ThrowIfCancellationRequested();
try
{
IEnumerable x = State.Where(func);
@@ -206,6 +212,9 @@ public async ValueTask DisposeAsync()
{
_cancelDaemon.Cancel();
await Stop().ConfigureAwait(false);
+ _cancelDaemon.Dispose();
+ await _scheduler.DisposeAsync().ConfigureAwait(false);
+ _cancelTokenSource?.Dispose();
Logger.LogTrace("Instance NetDaemonHost Disposed");
}
@@ -219,7 +228,9 @@ public void EnableApplicationDiscoveryService()
public IEntity Entities(INetDaemonApp app, Func func)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _ = app ??
+ throw new NetDaemonArgumentNullException(nameof(app));
+ _cancelToken.ThrowIfCancellationRequested();
try
{
@@ -234,22 +245,22 @@ public IEntity Entities(INetDaemonApp app, Func func)
}
}
- public IEntity Entities(INetDaemonApp app, IEnumerable entityIds)
+ public IEntity Entities(INetDaemonApp app, IEnumerable entityId)
{
- this._cancelToken.ThrowIfCancellationRequested();
- return new EntityManager(entityIds, this, app);
+ _cancelToken.ThrowIfCancellationRequested();
+ return new EntityManager(entityId, this, app);
}
- public IEntity Entity(INetDaemonApp app, params string[] entityIds)
+ public IEntity Entity(INetDaemonApp app, params string[] entityId)
{
- this._cancelToken.ThrowIfCancellationRequested();
- return new EntityManager(entityIds, this, app);
+ _cancelToken.ThrowIfCancellationRequested();
+ return new EntityManager(entityId, this, app);
}
///
public INetDaemonAppBase? GetApp(string appInstanceId)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return InternalRunningAppInstances.ContainsKey(appInstanceId) ?
InternalRunningAppInstances[appInstanceId] : null;
@@ -257,7 +268,7 @@ public IEntity Entity(INetDaemonApp app, params string[] entityIds)
public async Task GetDataAsync(string id) where T : class
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
_ = _repository as IDataRepository ??
throw new NetDaemonNullReferenceException($"{nameof(_repository)} can not be null!");
@@ -274,11 +285,13 @@ public IEntity Entity(INetDaemonApp app, params string[] entityIds)
return data;
}
- public EntityState? GetState(string entity)
+ public EntityState? GetState(string entityId)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _ = entityId ??
+ throw new NetDaemonArgumentNullException(nameof(entityId));
+ _cancelToken.ThrowIfCancellationRequested();
- return InternalState.TryGetValue(entity, out EntityState? returnValue)
+ return InternalState.TryGetValue(entityId, out EntityState? returnValue)
? returnValue
: null;
}
@@ -298,21 +311,21 @@ public async Task Initialize(IInstanceDaemonApp appInstanceManager)
///
public IFluentInputSelect InputSelect(INetDaemonApp app, params string[] inputSelectParams)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return new InputSelectManager(inputSelectParams, this, app);
}
///
public IFluentInputSelect InputSelects(INetDaemonApp app, IEnumerable inputSelectParams)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return new InputSelectManager(inputSelectParams, this, app);
}
///
public IFluentInputSelect InputSelects(INetDaemonApp app, Func func)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
IEnumerable x = State.Where(func).Select(n => n.EntityId);
return new InputSelectManager(x, this, app);
}
@@ -320,31 +333,41 @@ public IFluentInputSelect InputSelects(INetDaemonApp app, Func
public void ListenCompanionServiceCall(string service, Func action)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _ = service ??
+ throw new NetDaemonArgumentNullException(nameof(service));
+ _cancelToken.ThrowIfCancellationRequested();
_daemonServiceCallFunctions.Add(("netdaemon", service.ToLowerInvariant(), action));
}
public void ListenServiceCall(string domain, string service, Func action)
- => _daemonServiceCallFunctions.Add((domain.ToLowerInvariant(), service.ToLowerInvariant(), action));
+ {
+ _ = service ??
+ throw new NetDaemonArgumentNullException(nameof(service));
+ _ = domain ??
+ throw new NetDaemonArgumentNullException(nameof(domain));
+ _daemonServiceCallFunctions.Add((domain.ToLowerInvariant(), service.ToLowerInvariant(), action));
+ }
///
public IMediaPlayer MediaPlayer(INetDaemonApp app, params string[] entityIds)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return new MediaPlayerManager(entityIds, this, app);
}
///
public IMediaPlayer MediaPlayers(INetDaemonApp app, IEnumerable entityIds)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
return new MediaPlayerManager(entityIds, this, app);
}
///
public IMediaPlayer MediaPlayers(INetDaemonApp app, Func func)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _ = app ??
+ throw new NetDaemonArgumentNullException(nameof(app));
+ _cancelToken.ThrowIfCancellationRequested();
try
{
IEnumerable x = State.Where(func);
@@ -378,6 +401,7 @@ public async Task ReloadAllApps()
///
///
///
+ [SuppressMessage("", "CA1031")]
public async Task Run(string host, short port, bool ssl, string token, CancellationToken cancellationToken)
{
// Create combine cancellation token
@@ -444,16 +468,13 @@ public async Task Run(string host, short port, bool ssl, string token, Cancellat
HassEvent changedEvent = await _hassClient.ReadEventAsync(cancellationToken).ConfigureAwait(false);
if (changedEvent != null)
{
- if (changedEvent.Data is HassServiceEventData hseData)
- {
- if (hseData.Domain == "homeassistant" &&
+ if (changedEvent.Data is HassServiceEventData hseData && hseData.Domain == "homeassistant" &&
(hseData.Service == "stop" || hseData.Service == "restart"))
- {
- // The user stopped HA so just stop processing messages
- Logger.LogInformation("User {action} Home Assistant, will try to reconnect...",
- hseData.Service == "stop" ? "stopping" : "restarting");
- return;
- }
+ {
+ // The user stopped HA so just stop processing messages
+ Logger.LogInformation("User {action} Home Assistant, will try to reconnect...",
+ hseData.Service == "stop" ? "stopping" : "restarting");
+ return;
}
// Remove all completed Tasks
_eventHandlerTasks.RemoveAll(x => x.IsCompleted);
@@ -484,29 +505,30 @@ public async Task Run(string host, short port, bool ssl, string token, Cancellat
}
}
- public IScript RunScript(INetDaemonApp app, params string[] entityId)
+ public IScript RunScript(INetDaemonApp app, params string[] entityIds)
{
- this._cancelToken.ThrowIfCancellationRequested();
- return new EntityManager(entityId, this, app);
+ _cancelToken.ThrowIfCancellationRequested();
+ return new EntityManager(entityIds, this, app);
}
public Task SaveDataAsync(string id, T data)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
- _ = _repository as IDataRepository ??
+ _ = _repository ??
throw new NetDaemonNullReferenceException($"{nameof(_repository)} can not be null!");
if (data == null)
- throw new ArgumentNullException(nameof(data));
+ throw new NetDaemonArgumentNullException(nameof(data));
DataCache[id] = data;
return _repository!.Save(id, data);
}
+ [SuppressMessage("", "CA1031")]
public async Task SendEvent(string eventId, dynamic? data = null)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
if (!Connected)
return false;
@@ -524,7 +546,7 @@ public async Task SendEvent(string eventId, dynamic? data = null)
///
public async Task SetDaemonStateAsync(int numberOfLoadedApps, int numberOfRunningApps)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
await SetStateAsync(
"netdaemon.status",
@@ -536,7 +558,7 @@ public async Task SetDaemonStateAsync(int numberOfLoadedApps, int numberOfRunnin
public void SetState(string entityId, dynamic state, dynamic? attributes = null)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
if (!_setStateMessageChannel.Writer.TryWrite((entityId, state, attributes)))
throw new NetDaemonException("Servicecall queue full!");
@@ -545,7 +567,7 @@ public void SetState(string entityId, dynamic state, dynamic? attributes = null)
public async Task SetStateAsync(string entityId, dynamic state,
params (string name, object val)[] attributes)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
try
{
// Use expando object as all other methods
@@ -576,11 +598,12 @@ public void SetState(string entityId, dynamic state, dynamic? attributes = null)
public void Speak(string entityId, string message)
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
_ttsMessageChannel.Writer.TryWrite((entityId, message));
}
+ [SuppressMessage("", "CA1031")]
public async Task Stop()
{
try
@@ -687,21 +710,15 @@ internal static bool FixStateTypes(HassStateChangedEventData stateData)
internal string? GetAreaForEntityId(string entityId)
{
- if (_hassEntities.TryGetValue(entityId, out HassEntity? entity) && entity is not null)
+ if (_hassEntities.TryGetValue(entityId, out HassEntity? entity) && entity is not null && entity.DeviceId is not null)
{
- if (entity.DeviceId is not null)
+ // The entity is on a device
+ if (_hassDevices.TryGetValue(entity.DeviceId, out HassDevice? device) && device is not null && device.AreaId is not null)
{
- // The entity is on a device
- if (_hassDevices.TryGetValue(entity.DeviceId, out HassDevice? device) && device is not null)
+ // This device is in an area
+ if (_hassAreas.TryGetValue(device.AreaId, out HassArea? area) && area is not null)
{
- if (device.AreaId is not null)
- {
- // This device is in an area
- if (_hassAreas.TryGetValue(device.AreaId, out HassArea? area) && area is not null)
- {
- return area.Name;
- }
- }
+ return area.Name;
}
}
}
@@ -710,7 +727,7 @@ internal static bool FixStateTypes(HassStateChangedEventData stateData)
internal async Task RefreshInternalStatesAndSetArea()
{
- this._cancelToken.ThrowIfCancellationRequested();
+ _cancelToken.ThrowIfCancellationRequested();
foreach (var device in await _hassClient.GetDevices().ConfigureAwait(false))
{
@@ -769,9 +786,12 @@ internal static IList SortByDependency(IEnumerable await SetStateOnDaemonAppSwitch("on", data).ConfigureAwait(false));
@@ -1267,7 +1290,7 @@ async Task SetStateOnDaemonAppSwitch(string state, dynamic? data)
if (entityId is null)
return;
- if (!entityId.StartsWith("switch.netdaemon_"))
+ if (!entityId.StartsWith("switch.netdaemon_", true, CultureInfo.InvariantCulture))
return; // We only want app switches
await SetDependentState(entityId, state).ConfigureAwait(false);
@@ -1336,6 +1359,7 @@ private async Task PersistAppStateAsync(NetDaemonAppBase app)
await SaveDataAsync>(app.GetUniqueIdForStorage(), obj).ConfigureAwait(false);
}
+ [SuppressMessage("", "CA1031")]
private async Task RestoreAppState(INetDaemonAppBase appInstance)
{
try
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/Scheduler.cs b/src/Daemon/NetDaemon.Daemon/Daemon/Scheduler.cs
index d2ea08cd4..2e42a12c4 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/Scheduler.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/Scheduler.cs
@@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Threading;
@@ -62,6 +63,9 @@ public ISchedulerResult RunEvery(int millisecondsDelay, Func func)
///
public ISchedulerResult RunEvery(TimeSpan timeSpan, Func func)
{
+ _ = func ??
+ throw new NetDaemonArgumentNullException(nameof(func));
+
var cancelSource = new CancellationTokenSource();
var task = RunEveryInternalAsync(timeSpan, func, cancelSource.Token);
@@ -70,6 +74,7 @@ public ISchedulerResult RunEvery(TimeSpan timeSpan, Func func)
return new SchedulerResult(task, cancelSource);
}
+ [SuppressMessage("", "CA1031")]
private async Task RunEveryInternalAsync(TimeSpan timeSpan, Func func, CancellationToken token)
{
using CancellationTokenSource linkedCts =
@@ -131,6 +136,9 @@ internal TimeSpan CalculateEveryMinuteTimeBetweenNowAndTargetTime(short second)
///
public ISchedulerResult RunDaily(string time, IEnumerable? runOnDays, Func func)
{
+ _ = func ??
+ throw new NetDaemonArgumentNullException(nameof(func));
+
var cancelSource = new CancellationTokenSource();
if (!DateTime.TryParseExact(time, "HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime timeOfDayToTrigger))
@@ -144,6 +152,7 @@ public ISchedulerResult RunDaily(string time, IEnumerable? runOnDays,
return new SchedulerResult(task, cancelSource);
}
+ [SuppressMessage("", "CA1031")]
private async Task RunDailyInternalAsync(DateTime timeOfDayToTrigger, IEnumerable? runOnDays, Func func, CancellationToken token)
{
using CancellationTokenSource linkedCts =
@@ -193,6 +202,8 @@ private async Task RunDailyInternalAsync(DateTime timeOfDayToTrigger, IEnumerabl
///
public ISchedulerResult RunEveryMinute(short second, Func func)
{
+ _ = func ??
+ throw new NetDaemonArgumentNullException(nameof(func));
var cancelSource = new CancellationTokenSource();
var task = RunEveryMinuteInternalAsync(second, func, cancelSource.Token);
@@ -201,6 +212,7 @@ public ISchedulerResult RunEveryMinute(short second, Func func)
return new SchedulerResult(task, cancelSource);
}
+ [SuppressMessage("", "CA1031")]
private async Task RunEveryMinuteInternalAsync(short second, Func func, CancellationToken token)
{
using CancellationTokenSource linkedCts =
@@ -223,11 +235,15 @@ private async Task RunEveryMinuteInternalAsync(short second, Func func, Ca
}
///
+ [SuppressMessage("", "CA1031")]
public ISchedulerResult RunIn(int millisecondsDelay, Func func) => RunIn(TimeSpan.FromMilliseconds(millisecondsDelay), func);
///
public ISchedulerResult RunIn(TimeSpan timeSpan, Func func)
{
+ _ = func ??
+ throw new NetDaemonArgumentNullException(nameof(func));
+
var cancelSource = new CancellationTokenSource();
var task = InternalRunInAsync(timeSpan, func, cancelSource.Token);
ScheduleTask(task);
@@ -235,6 +251,7 @@ public ISchedulerResult RunIn(TimeSpan timeSpan, Func func)
return new SchedulerResult(task, cancelSource);
}
+ [SuppressMessage("", "CA1031")]
private async Task InternalRunInAsync(TimeSpan timeSpan, Func func, CancellationToken token)
{
using CancellationTokenSource linkedCts =
@@ -265,6 +282,8 @@ public async Task Stop()
var taskResult = await Task.WhenAny(
Task.WhenAll(_scheduledTasks.Values.ToArray()), Task.Delay(1000)).ConfigureAwait(false);
+ _cancelSource.Dispose();
+
if (_scheduledTasks.Values.Any(n => !n.IsCompleted))
{
// Todo: Some kind of logging have to be done here to tell user which task caused timeout
@@ -316,6 +335,7 @@ private void ScheduleTask(Task addedTask)
_scheduledTasks[addedTask.Id] = addedTask;
}
+ [SuppressMessage("", "CA1031")]
public async ValueTask DisposeAsync()
{
try
@@ -337,7 +357,6 @@ public class TimeManager : IManageTime
///
/// Returns current local time
///
- ///
public DateTime Current => DateTime.Now;
///
diff --git a/src/Daemon/NetDaemon.Daemon/Daemon/Storage/DataRepository.cs b/src/Daemon/NetDaemon.Daemon/Daemon/Storage/DataRepository.cs
index c784bc044..8ffb496e6 100644
--- a/src/Daemon/NetDaemon.Daemon/Daemon/Storage/DataRepository.cs
+++ b/src/Daemon/NetDaemon.Daemon/Daemon/Storage/DataRepository.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text.Json;
@@ -23,6 +24,7 @@ public DataRepository(string dataStoragePath)
}
///
+ [SuppressMessage("", "CA1031")]
public async ValueTask Get(string id) where T : class
{
try
diff --git a/src/Daemon/NetDaemon.Daemon/Mapping/EntityStateMapper.cs b/src/Daemon/NetDaemon.Daemon/Mapping/EntityStateMapper.cs
index 409a77be9..5ca4ca720 100644
--- a/src/Daemon/NetDaemon.Daemon/Mapping/EntityStateMapper.cs
+++ b/src/Daemon/NetDaemon.Daemon/Mapping/EntityStateMapper.cs
@@ -4,6 +4,7 @@
using JoySoftware.HomeAssistant.Client;
using NetDaemon.Infrastructure.Extensions;
using NetDaemon.Common;
+using NetDaemon.Common.Exceptions;
namespace NetDaemon.Mapping
{
@@ -13,9 +14,10 @@ public static class EntityStateMapper
/// Converts HassState to EntityState
///
///
- ///
public static EntityState Map(this HassState hassState)
{
+ _ = hassState ??
+ throw new NetDaemonArgumentNullException(nameof(hassState));
var entityState = new EntityState
{
EntityId = hassState.EntityId,
diff --git a/src/Daemon/NetDaemon.Daemon/NetDaemon.Daemon.csproj b/src/Daemon/NetDaemon.Daemon/NetDaemon.Daemon.csproj
index 667c9b43a..8faf00c9a 100644
--- a/src/Daemon/NetDaemon.Daemon/NetDaemon.Daemon.csproj
+++ b/src/Daemon/NetDaemon.Daemon/NetDaemon.Daemon.csproj
@@ -25,6 +25,10 @@
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
@@ -33,4 +37,9 @@
..\..\..\.linting\roslynator.ruleset
+
+ ..\..\..\.linting\roslynator.ruleset
+ true
+ AllEnabledByDefault
+
\ No newline at end of file
diff --git a/tests/NetDaemon.Daemon.Tests/Daemon/DataRepositoryTests.cs b/tests/NetDaemon.Daemon.Tests/Daemon/DataRepositoryTests.cs
index caad4c270..31286a7f2 100644
--- a/tests/NetDaemon.Daemon.Tests/Daemon/DataRepositoryTests.cs
+++ b/tests/NetDaemon.Daemon.Tests/Daemon/DataRepositoryTests.cs
@@ -16,7 +16,7 @@ public class DataRepositoryTests : DaemonHostTestBase
public static readonly string DataRepositoryPath =
Path.Combine(AppContext.BaseDirectory, "datarepository");
- public DataRepositoryTests() : base()
+ public DataRepositoryTests()
{
}