mirror of
https://github.com/jellyfin/jellyfin-plugin-webhook.git
synced 2025-02-17 05:27:38 +00:00
Added MQTT as a destination (#134)
Co-authored-by: Darick Carpenter <carpenterd@byui.edu>
This commit is contained in:
parent
3ae6c098bd
commit
4ec22bd5a2
@ -3,6 +3,7 @@ using Jellyfin.Plugin.Webhook.Destinations.Discord;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Generic;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.GenericForm;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Gotify;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Pushbullet;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Pushover;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Slack;
|
||||
@ -30,6 +31,7 @@ namespace Jellyfin.Plugin.Webhook.Configuration
|
||||
PushoverOptions = Array.Empty<PushoverOption>();
|
||||
SlackOptions = Array.Empty<SlackOption>();
|
||||
SmtpOptions = Array.Empty<SmtpOption>();
|
||||
MqttOptions = Array.Empty<MqttOption>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -76,5 +78,10 @@ namespace Jellyfin.Plugin.Webhook.Configuration
|
||||
/// Gets or sets the smtp options.
|
||||
/// </summary>
|
||||
public SmtpOption[] SmtpOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mqtt options.
|
||||
/// </summary>
|
||||
public MqttOption[] MqttOptions { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<form class="webhookConfigurationForm">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtServerUrl" required="required" label="Server Url:"/>
|
||||
<input is="emby-input" type="text" id="txtServerUrl" required="required" label="Server Url:" />
|
||||
<span>For linking to content. Include base url.</span>
|
||||
</div>
|
||||
<button id="btnAddDiscord" is="emby-button" type="button" class="raised button block">
|
||||
@ -41,9 +41,12 @@
|
||||
<button id="btnAddSmtp" is="emby-button" type="button" class="raised button block">
|
||||
<span>Add SMTP Destination</span>
|
||||
</button>
|
||||
<br/>
|
||||
<button id="btnAddMqtt" is="emby-button" type="button" class="raised button block">
|
||||
<span>Add MQTT Destination</span>
|
||||
</button>
|
||||
<br />
|
||||
<div id="configurationWrapper"></div>
|
||||
<br/>
|
||||
<br />
|
||||
<button id="saveConfig" is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>Save</span>
|
||||
</button>
|
||||
@ -248,46 +251,79 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="template-smtp">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtSenderAddress" label="Sender Address:"/>
|
||||
<template id="template-smtp">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtSenderAddress" label="Sender Address:"/>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtReceiverAddress" label="Receiver Address:"/>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtSmtpServer" label="SMTP Server Address:"/>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" data-name="txtSmtpPort" label="SMTP Port:"/>
|
||||
</div>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkUseCredentials"/>
|
||||
<span>Use Credentials</span>
|
||||
</label>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtUsername" label="Username:"/>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtPassword" label="Password:"/>
|
||||
</div>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkUseSsl"/>
|
||||
<span>Use SSL</span>
|
||||
</label>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkIsHtml"/>
|
||||
<span>Is Html Body</span>
|
||||
</label>
|
||||
<div class="inputContainer">
|
||||
<label>Subject Template:</label>
|
||||
<div>
|
||||
<textarea data-name="txtSubjectTemplate" style="width: 100%; height: 400px"></textarea>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtReceiverAddress" label="Receiver Address:"/>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtSmtpServer" label="SMTP Server Address:"/>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" data-name="txtSmtpPort" label="SMTP Port:"/>
|
||||
</div>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkUseCredentials"/>
|
||||
<span>Use Credentials</span>
|
||||
</label>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtUsername" label="Username:"/>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtPassword" label="Password:"/>
|
||||
</div>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkUseSsl"/>
|
||||
<span>Use SSL</span>
|
||||
</label>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkIsHtml"/>
|
||||
<span>Is Html Body</span>
|
||||
</label>
|
||||
<div class="inputContainer">
|
||||
<label>Subject Template:</label>
|
||||
<div>
|
||||
<textarea data-name="txtSubjectTemplate" style="width: 100%; height: 400px"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="template-pushbullet">
|
||||
<template id="template-mqtt">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtMqttServer" label="MQTT Server:" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" data-name="txtMqttPort" label="MQTT Port (default is 1883):" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtTopic" label="Topic:" />
|
||||
</div>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkUseCredentials" />
|
||||
<span>Use Credentials</span>
|
||||
</label>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtUsername" label="Username:" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtPassword" label="Password:" />
|
||||
</div>
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" data-name="chkUseTls" />
|
||||
<span>Use TLS</span>
|
||||
</label>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" data-name="ddlQosLevel" label="Quality of Service:">
|
||||
<option value="AtMostOnce">At Most Once</option>
|
||||
<option value="AtLeastOnce">At Least Once</option>
|
||||
<option value="ExactlyOnce">Exactly Once</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="template-pushbullet">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" data-name="txtToken" label="Token:"/>
|
||||
</div>
|
||||
|
@ -573,6 +573,45 @@
|
||||
return config;
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
btnAdd: document.querySelector("#btnAddMqtt"),
|
||||
template: document.querySelector("#template-mqtt"),
|
||||
addConfig: function (config) {
|
||||
const template = document.createElement("div");
|
||||
template.dataset.type = "mqtt";
|
||||
template.appendChild(Webhook.baseConfig.template.cloneNode(true).content);
|
||||
template.appendChild(Webhook.mqtt.template.cloneNode(true).content);
|
||||
|
||||
const baseConfig = Webhook.baseConfig.addConfig(template, "MQTT", config.WebhookName);
|
||||
Webhook.configurationWrapper.appendChild(baseConfig);
|
||||
|
||||
// Load configuration
|
||||
Webhook.mqtt.setConfig(config, baseConfig);
|
||||
},
|
||||
setConfig: function (config, element) {
|
||||
Webhook.baseConfig.setConfig(config, element);
|
||||
element.querySelector("[data-name=txtMqttServer]").value = config.MqttServer || "";
|
||||
element.querySelector("[data-name=txtMqttPort]").value = config.MqttPort || 1883;
|
||||
element.querySelector("[data-name=txtTopic]").value = Webhook.atou(config.Topic || "");
|
||||
element.querySelector("[data-name=chkUseCredentials]").checked = config.UseCredentials || false;
|
||||
element.querySelector("[data-name=txtUsername]").value = config.Username || "";
|
||||
element.querySelector("[data-name=txtPassword]").value = config.Password || "";
|
||||
element.querySelector("[data-name=chkUseTls]").checked = config.UseTls || false;
|
||||
element.querySelector("[data-name=ddlQosLevel]").value = config.QosLevel || "AtMostOnce";
|
||||
},
|
||||
getConfig: function (element) {
|
||||
const config = Webhook.baseConfig.getConfig(element);
|
||||
config.MqttServer = element.querySelector("[data-name=txtMqttServer]").value || "";
|
||||
config.MqttPort = element.querySelector("[data-name=txtMqttPort]").value || 1883;
|
||||
config.Topic = Webhook.utoa(element.querySelector("[data-name=txtTopic]").value || "");
|
||||
config.UseCredentials = element.querySelector("[data-name=chkUseCredentials]").checked || false;
|
||||
config.Username = element.querySelector("[data-name=txtUsername]").value || "";
|
||||
config.Password = element.querySelector("[data-name=txtPassword]").value || "";
|
||||
config.UseTls = element.querySelector("[data-name=chkUseTls]").checked || false;
|
||||
config.QosLevel = element.querySelector("[data-name=ddlQosLevel]").value || "";
|
||||
return config;
|
||||
}
|
||||
},
|
||||
init: async function () {
|
||||
// Clear any previously loaded configuration.
|
||||
Webhook.configurationWrapper.innerHTML = "";
|
||||
@ -586,6 +625,7 @@
|
||||
Webhook.pushover.btnAdd.addEventListener("click", Webhook.pushover.addConfig);
|
||||
Webhook.slack.btnAdd.addEventListener("click", Webhook.slack.addConfig);
|
||||
Webhook.smtp.btnAdd.addEventListener("click", Webhook.smtp.addConfig);
|
||||
Webhook.mqtt.btnAdd.addEventListener("click", Webhook.mqtt.addConfig);
|
||||
document.querySelector("#saveConfig").addEventListener("click", Webhook.saveConfig);
|
||||
|
||||
await Webhook.userFilter.populate();
|
||||
@ -650,6 +690,12 @@
|
||||
config.SmtpOptions.push(Webhook.smtp.getConfig(smtpConfigs[i]));
|
||||
}
|
||||
|
||||
config.MqttOptions = [];
|
||||
const mqttConfigs = document.querySelectorAll("[data-type=mqtt]");
|
||||
for (let i = 0; i < mqttConfigs.length; i++) {
|
||||
config.MqttOptions.push(Webhook.mqtt.getConfig(mqttConfigs[i]));
|
||||
}
|
||||
|
||||
window.ApiClient.updatePluginConfiguration(Webhook.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
|
||||
},
|
||||
loadConfig: function () {
|
||||
@ -688,6 +734,10 @@
|
||||
for (let i = 0; i < config.SmtpOptions.length; i++) {
|
||||
Webhook.smtp.addConfig(config.SmtpOptions[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < config.MqttOptions.length; i++) {
|
||||
Webhook.mqtt.addConfig(config.MqttOptions[i]);
|
||||
}
|
||||
});
|
||||
|
||||
Dashboard.hideLoadingMsg()
|
||||
|
25
Jellyfin.Plugin.Webhook/Destinations/Mqtt/IMqttClients.cs
Normal file
25
Jellyfin.Plugin.Webhook/Destinations/Mqtt/IMqttClients.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
|
||||
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// IMqttClients.
|
||||
/// </summary>
|
||||
public interface IMqttClients
|
||||
{
|
||||
/// <summary>
|
||||
/// Update Clients with options.
|
||||
/// </summary>
|
||||
/// <param name="options">Instance of the <see cref="MqttOption"/>.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task UpdateClients(MqttOption[] options);
|
||||
|
||||
/// <summary>
|
||||
/// Get Managed Mqtt Clients.
|
||||
/// </summary>
|
||||
/// <param name="guid">guid of MqttOption.</param>
|
||||
/// <returns>Instance of the <see cref="IManagedMqttClient"/> interface.</returns>
|
||||
IManagedMqttClient? GetClient(Guid guid);
|
||||
}
|
55
Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClient.cs
Normal file
55
Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClient.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
|
||||
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// Client for the <see cref="MqttOption"/>.
|
||||
/// </summary>
|
||||
public class MqttClient : BaseClient, IWebhookClient<MqttOption>
|
||||
{
|
||||
private readonly ILogger<MqttClient> _logger;
|
||||
private readonly IMqttClients _mqttClients;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MqttClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{MqttClient}"/> interface.</param>
|
||||
/// <param name="mqttClients">Instance of the <see cref="IMqttClients"/> interface.</param>
|
||||
public MqttClient(ILogger<MqttClient> logger, IMqttClients mqttClients)
|
||||
{
|
||||
_logger = logger;
|
||||
_mqttClients = mqttClients;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task SendAsync(MqttOption option, Dictionary<string, object> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!SendWebhook(_logger, option, data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var body = option.GetMessageBody(data);
|
||||
var client = _mqttClients.GetClient(option.Guid);
|
||||
if (client?.IsConnected != true)
|
||||
{
|
||||
_logger.LogDebug("MQTT error, not connected {@server}", option.MqttServer);
|
||||
return;
|
||||
}
|
||||
|
||||
var topic = option.GetCompiledTopicTemplate()(data);
|
||||
|
||||
await client.EnqueueAsync(topic, body).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogDebug(e, "Error sending MQTT notification");
|
||||
}
|
||||
}
|
||||
}
|
134
Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClients.cs
Normal file
134
Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttClients.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
|
||||
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// MqttClients.
|
||||
/// </summary>
|
||||
public class MqttClients : IMqttClients, IDisposable
|
||||
{
|
||||
private readonly ILogger<MqttClients> _logger;
|
||||
private readonly Dictionary<Guid, IManagedMqttClient> _managedMqttClients = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MqttClients"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{MqttClients}"/> interface.</param>
|
||||
public MqttClients(ILogger<MqttClients> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task UpdateClients(MqttOption[] options)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var client in _managedMqttClients.Values.Where(c => c.IsConnected))
|
||||
{
|
||||
await client.StopAsync().ConfigureAwait(false);
|
||||
client.ConnectingFailedAsync -= Client_ConnectingFailedAsync;
|
||||
client.ConnectedAsync -= Client_ConnectedAsync;
|
||||
client.DisconnectedAsync -= Client_DisconnectedAsync;
|
||||
}
|
||||
|
||||
_managedMqttClients.Clear();
|
||||
|
||||
foreach (var option in options)
|
||||
{
|
||||
var messageBuilder = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer(option.MqttServer, option.MqttPort)
|
||||
.WithWillQualityOfServiceLevel(option.QosLevel)
|
||||
.WithClientId(option.Guid.ToString());
|
||||
|
||||
if (option.UseCredentials && !string.IsNullOrEmpty(option.Username) && !string.IsNullOrEmpty(option.Password))
|
||||
{
|
||||
messageBuilder.WithCredentials(option.Username, option.Password);
|
||||
}
|
||||
|
||||
if (option.UseTls)
|
||||
{
|
||||
messageBuilder.WithTls();
|
||||
}
|
||||
|
||||
var clientOptions = messageBuilder.Build();
|
||||
|
||||
var managedOptions = new ManagedMqttClientOptionsBuilder()
|
||||
.WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
|
||||
.WithClientOptions(clientOptions)
|
||||
.Build();
|
||||
|
||||
var client = new MqttFactory().CreateManagedMqttClient();
|
||||
client.ConnectingFailedAsync += Client_ConnectingFailedAsync;
|
||||
client.ConnectedAsync += Client_ConnectedAsync;
|
||||
client.DisconnectedAsync += Client_DisconnectedAsync;
|
||||
_managedMqttClients.Add(option.Guid, client);
|
||||
|
||||
await client.StartAsync(managedOptions).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogWarning(e, "Error adding/starting MQTT Clients");
|
||||
}
|
||||
}
|
||||
|
||||
private Task Client_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
|
||||
{
|
||||
var message = arg.Reason;
|
||||
_logger.LogDebug(arg.Exception, "MQTT Client disconnected. Exception: {@message}", message);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task Client_ConnectedAsync(MqttClientConnectedEventArgs arg)
|
||||
{
|
||||
_logger.LogDebug("MQTT Client connected.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task Client_ConnectingFailedAsync(ConnectingFailedEventArgs arg)
|
||||
{
|
||||
_logger.LogDebug(arg.Exception, "MQTT Client connection failed.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IManagedMqttClient? GetClient(Guid guid)
|
||||
{
|
||||
if (_managedMqttClients.TryGetValue(guid, out var client))
|
||||
{
|
||||
return client;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose Objects.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose.
|
||||
/// </summary>
|
||||
/// <param name="disposeAll">somthing.</param>
|
||||
protected virtual void Dispose(bool disposeAll)
|
||||
{
|
||||
foreach (var client in _managedMqttClients.Values)
|
||||
{
|
||||
client.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
73
Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttOption.cs
Normal file
73
Jellyfin.Plugin.Webhook/Destinations/Mqtt/MqttOption.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using HandlebarsDotNet;
|
||||
using Jellyfin.Plugin.Webhook.Helpers;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// Mqtt specific option.
|
||||
/// </summary>
|
||||
public class MqttOption : BaseOption
|
||||
{
|
||||
private HandlebarsTemplate<object, string>? _compiledTopicTemplate;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MqttOption"/> class.
|
||||
/// </summary>
|
||||
public MqttOption()
|
||||
{
|
||||
Guid = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Guid for MqttOption.
|
||||
/// </summary>
|
||||
public Guid Guid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MqttServer.
|
||||
/// </summary>
|
||||
public string MqttServer { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mqtt port.
|
||||
/// </summary>
|
||||
public int MqttPort { get; set; } = 1883;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the BaseTopic.
|
||||
/// </summary>
|
||||
public string Topic { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use credentials.
|
||||
/// </summary>
|
||||
public bool UseCredentials { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the username.
|
||||
/// </summary>
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use TLS.
|
||||
/// </summary>
|
||||
public bool UseTls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Quality of service level.
|
||||
/// </summary>
|
||||
public MqttQualityOfServiceLevel QosLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compiled handlebars topic template.
|
||||
/// </summary>
|
||||
/// <returns>The compiled handlebars subject template.</returns>
|
||||
public HandlebarsTemplate<object, string> GetCompiledTopicTemplate() => _compiledTopicTemplate ??= Handlebars.Compile(HandlebarsFunctionHelpers.Base64Decode(Topic));
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
<PackageReference Include="MailKit" Version="3.4.1" />
|
||||
<PackageReference Include="Handlebars.Net" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.*" />
|
||||
<PackageReference Include="MQTTnet.Extensions.ManagedClient" Version="4.1.0.247" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -6,6 +6,7 @@ using Jellyfin.Plugin.Webhook.Destinations.Discord;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Generic;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.GenericForm;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Gotify;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Pushbullet;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Pushover;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Slack;
|
||||
@ -42,10 +43,14 @@ namespace Jellyfin.Plugin.Webhook
|
||||
serviceCollection.AddScoped<IWebhookClient<PushoverOption>, PushoverClient>();
|
||||
serviceCollection.AddScoped<IWebhookClient<SlackOption>, SlackClient>();
|
||||
serviceCollection.AddScoped<IWebhookClient<SmtpOption>, SmtpClient>();
|
||||
serviceCollection.AddScoped<IWebhookClient<MqttOption>, MqttClient>();
|
||||
|
||||
// Register sender.
|
||||
serviceCollection.AddScoped<IWebhookSender, WebhookSender>();
|
||||
|
||||
// Register MqttClients
|
||||
serviceCollection.AddSingleton<IMqttClients, MqttClients>();
|
||||
|
||||
/*-- Register event consumers. --*/
|
||||
// Library consumers.
|
||||
serviceCollection.AddScoped<IEventConsumer<SubtitleDownloadFailureEventArgs>, SubtitleDownloadFailureNotifier>();
|
||||
|
28
Jellyfin.Plugin.Webhook/Templates/Mqtt.handlebars
Normal file
28
Jellyfin.Plugin.Webhook/Templates/Mqtt.handlebars
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"DeviceId": "{{DeviceId}}",
|
||||
"DeviceName": "{{DeviceName}}",
|
||||
"ClientName": "{{ClientName}}",
|
||||
"ItemId": "{{ItemId}}",
|
||||
"RunTime": "{{RunTime}}",
|
||||
"PlaybackPosition": "{{PlaybackPosition}}",
|
||||
{{#if_equals NotificationType 'PlaybackStart'}}
|
||||
"url": "{{ServerUrl}}/web/index.html#!/details?id={{ItemId}}&serverId={{ServerId}}",
|
||||
"thumbnail": "{{ServerUrl}}/Items/{{ItemId}}/Images/Primary",
|
||||
"backdrop": "{{ServerUrl}}/Items/{{ItemId}}/Images/Backdrop",
|
||||
"logo": "{{ServerUrl}}/Items/{{ItemId}}/Images/Logo",
|
||||
"Name": "{{Name}}",
|
||||
{{#if_equals ItemType 'Episode'}}
|
||||
"SeriesName": "{{SeriesName}}",
|
||||
"SeasonNumber": "{{SeasonNumber}}",
|
||||
"EpisodeNumber": "{{EpisodeNumber}}"
|
||||
{{else}}
|
||||
"Year": {{Year}}
|
||||
{{/if_equals}}
|
||||
{{/if_equals}}
|
||||
{{#if_equals NotificationType 'PlaybackStop'}}
|
||||
"PlayedToCompletion": "{{PlayedToCompletion}}"
|
||||
{{/if_equals}}
|
||||
{{#if_equals NotificationType 'PlaybackProgress'}}
|
||||
"IsPaused": "{{IsPaused}}"
|
||||
{{/if_equals}}
|
||||
}
|
@ -8,6 +8,7 @@ using Jellyfin.Plugin.Webhook.Destinations.Discord;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Generic;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.GenericForm;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Gotify;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Pushbullet;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Pushover;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Slack;
|
||||
@ -31,6 +32,7 @@ namespace Jellyfin.Plugin.Webhook
|
||||
private readonly IWebhookClient<PushoverOption> _pushoverClient;
|
||||
private readonly IWebhookClient<SlackOption> _slackClient;
|
||||
private readonly IWebhookClient<SmtpOption> _smtpClient;
|
||||
private readonly IWebhookClient<MqttOption> _mqttClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebhookSender"/> class.
|
||||
@ -44,6 +46,7 @@ namespace Jellyfin.Plugin.Webhook
|
||||
/// <param name="pushoverClient">Instance of the <see cref="IWebhookClient{PushoverOption}"/>.</param>
|
||||
/// <param name="slackClient">Instance of the <see cref="IWebhookClient{SlackOption}"/>.</param>
|
||||
/// <param name="smtpClient">Instance of the <see cref="IWebhookClient{SmtpOption}"/>.</param>
|
||||
/// <param name="mqttClient">Instance of the <see cref="IWebhookClient{mqttClient}"/>.</param>
|
||||
public WebhookSender(
|
||||
ILogger<WebhookSender> logger,
|
||||
IWebhookClient<DiscordOption> discordClient,
|
||||
@ -53,7 +56,8 @@ namespace Jellyfin.Plugin.Webhook
|
||||
IWebhookClient<PushbulletOption> pushbulletClient,
|
||||
IWebhookClient<PushoverOption> pushoverClient,
|
||||
IWebhookClient<SlackOption> slackClient,
|
||||
IWebhookClient<SmtpOption> smtpClient)
|
||||
IWebhookClient<SmtpOption> smtpClient,
|
||||
IWebhookClient<MqttOption> mqttClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_discordClient = discordClient;
|
||||
@ -64,6 +68,7 @@ namespace Jellyfin.Plugin.Webhook
|
||||
_pushoverClient = pushoverClient;
|
||||
_slackClient = slackClient;
|
||||
_smtpClient = smtpClient;
|
||||
_mqttClient = mqttClient;
|
||||
}
|
||||
|
||||
private static PluginConfiguration Configuration =>
|
||||
@ -119,6 +124,12 @@ namespace Jellyfin.Plugin.Webhook
|
||||
await SendNotification(_smtpClient, option, itemData, itemType)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
foreach (var option in Configuration.MqttOptions.Where(o => o.NotificationTypes.Contains(notificationType)))
|
||||
{
|
||||
await SendNotification(_mqttClient, option, itemData, itemType)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool NotifyOnItem<T>(T baseOptions, Type? itemType)
|
||||
|
61
Jellyfin.Plugin.Webhook/WebhookServerEntryPoint.cs
Normal file
61
Jellyfin.Plugin.Webhook/WebhookServerEntryPoint.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Plugin.Webhook.Configuration;
|
||||
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace Jellyfin.Plugin.Webhook;
|
||||
|
||||
/// <summary>
|
||||
/// WebhookServerEntryPoint.
|
||||
/// </summary>
|
||||
public class WebhookServerEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private readonly IMqttClients _mqttClients;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebhookServerEntryPoint"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mqttClients">Instance of the <see cref="IMqttClients"/> interface.</param>
|
||||
public WebhookServerEntryPoint(IMqttClients mqttClients)
|
||||
{
|
||||
_mqttClients = mqttClients;
|
||||
WebhookPlugin.Instance!.ConfigurationChanged += ConfigurationChanged;
|
||||
}
|
||||
|
||||
private static PluginConfiguration Configuration =>
|
||||
WebhookPlugin.Instance!.Configuration;
|
||||
|
||||
private async void ConfigurationChanged(object? sender, BasePluginConfiguration e)
|
||||
{
|
||||
await RunAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose Objects.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose.
|
||||
/// </summary>
|
||||
/// <param name="disposeAll">Dispose of objects.</param>
|
||||
protected virtual void Dispose(bool disposeAll)
|
||||
{
|
||||
WebhookPlugin.Instance!.ConfigurationChanged -= ConfigurationChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run this.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
public async Task RunAsync()
|
||||
{
|
||||
await _mqttClients.UpdateClients(Configuration.MqttOptions).ConfigureAwait(false);
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ artifacts:
|
||||
- "MailKit.dll"
|
||||
- "MimeKit.dll"
|
||||
- "BouncyCastle.Crypto.dll"
|
||||
- "MQTTnet.dll"
|
||||
- "MQTTnet.Extensions.ManagedClient.dll"
|
||||
changelog: |-
|
||||
### Code or Repo Maintenance ###
|
||||
- Fix scheduled task (#106) @crobibero
|
||||
|
Loading…
x
Reference in New Issue
Block a user