2024-06-24 09:51:59 -07:00
import type { ISubscriptionMap } from 'mqtt' ;
import type { QoS } from 'mqtt-packet' ;
2023-03-09 09:13:15 -08:00
import type {
ITriggerFunctions ,
IDataObject ,
INodeType ,
INodeTypeDescription ,
ITriggerResponse ,
2023-08-16 04:06:47 -07:00
IRun ,
2023-03-09 09:13:15 -08:00
} from 'n8n-workflow' ;
2024-06-24 09:51:59 -07:00
import { NodeOperationError } from 'n8n-workflow' ;
2020-09-01 08:40:18 -07:00
2024-06-24 09:51:59 -07:00
import { createClient , type MqttCredential } from './GenericFunctions' ;
interface Options {
jsonParseBody : boolean ;
onlyMessage : boolean ;
parallelProcessing : boolean ;
}
2020-09-01 08:40:18 -07:00
export class MqttTrigger implements INodeType {
description : INodeTypeDescription = {
displayName : 'MQTT Trigger' ,
name : 'mqttTrigger' ,
2021-04-30 18:23:25 -07:00
icon : 'file:mqtt.svg' ,
2020-09-01 08:40:18 -07:00
group : [ 'trigger' ] ,
version : 1 ,
description : 'Listens to MQTT events' ,
2023-06-23 03:29:24 -07:00
eventTriggerDescription : '' ,
2020-09-01 08:40:18 -07:00
defaults : {
name : 'MQTT Trigger' ,
} ,
2023-06-23 03:29:24 -07:00
triggerPanel : {
header : '' ,
executionsHelp : {
inactive :
"<b>While building your workflow</b>, click the 'listen' button, then trigger an MQTT event. This will trigger an execution, which will show up in this editor.<br /> <br /><b>Once you're happy with your workflow</b>, <a data-key='activate'>activate</a> it. Then every time a change is detected, the workflow will execute. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor." ,
active :
"<b>While building your workflow</b>, click the 'listen' button, then trigger an MQTT event. This will trigger an execution, which will show up in this editor.<br /> <br /><b>Your workflow will also execute automatically</b>, since it's activated. Every time a change is detected, this node will trigger an execution. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor." ,
} ,
activationHint :
"Once you’ ve finished building your workflow, <a data-key='activate'>activate</a> it to have it also listen continuously (you just won’ t see those executions here)." ,
} ,
2020-09-01 08:40:18 -07:00
inputs : [ ] ,
outputs : [ 'main' ] ,
credentials : [
{
name : 'mqtt' ,
required : true ,
} ,
] ,
properties : [
{
displayName : 'Topics' ,
name : 'topics' ,
type : 'string' ,
default : '' ,
2022-08-17 08:50:24 -07:00
description :
'Topics to subscribe to, multiple can be defined with comma. Wildcard characters are supported (+ - for single level and # - for multi level). By default all subscription used QoS=0. To set a different QoS, write the QoS desired after the topic preceded by a colom. For Example: topicA:1,topicB:2' ,
2020-09-01 08:40:18 -07:00
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
placeholder : 'Add Option' ,
default : { } ,
options : [
{
2021-04-30 18:23:25 -07:00
displayName : 'JSON Parse Body' ,
name : 'jsonParseBody' ,
2020-09-01 08:40:18 -07:00
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to try parse the message to an object' ,
2020-09-01 08:40:18 -07:00
} ,
{
2021-04-30 18:23:25 -07:00
displayName : 'Only Message' ,
name : 'onlyMessage' ,
2020-09-01 08:40:18 -07:00
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to return only the message property' ,
2020-09-01 08:40:18 -07:00
} ,
2023-08-16 04:06:47 -07:00
{
displayName : 'Parallel Processing' ,
name : 'parallelProcessing' ,
type : 'boolean' ,
default : true ,
description :
'Whether to process messages in parallel or by keeping the message in order' ,
} ,
2020-09-01 08:40:18 -07:00
] ,
} ,
] ,
} ;
async trigger ( this : ITriggerFunctions ) : Promise < ITriggerResponse > {
const topics = ( this . getNodeParameter ( 'topics' ) as string ) . split ( ',' ) ;
2024-06-24 09:51:59 -07:00
if ( ! topics ? . length ) {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , 'Topics are mandatory!' ) ;
2020-09-01 08:40:18 -07:00
}
2024-06-24 09:51:59 -07:00
const topicsQoS : ISubscriptionMap = { } ;
for ( const data of topics ) {
const [ topic , qosString ] = data . split ( ':' ) ;
let qos = qosString ? parseInt ( qosString , 10 ) : 0 ;
if ( qos < 0 || qos > 2 ) qos = 0 ;
topicsQoS [ topic ] = { qos : qos as QoS } ;
2020-09-01 08:40:18 -07:00
}
2024-06-24 09:51:59 -07:00
const options = this . getNodeParameter ( 'options' ) as Options ;
const credentials = ( await this . getCredentials ( 'mqtt' ) ) as unknown as MqttCredential ;
const client = await createClient ( credentials ) ;
2020-09-01 08:40:18 -07:00
2024-06-24 09:51:59 -07:00
const parsePayload = ( topic : string , payload : Buffer ) = > {
let message = payload . toString ( ) ;
2020-09-01 08:40:18 -07:00
2024-06-24 09:51:59 -07:00
if ( options . jsonParseBody ) {
try {
message = JSON . parse ( message ) ;
} catch ( e ) { }
}
2020-09-01 08:40:18 -07:00
2024-06-24 09:51:59 -07:00
let result : IDataObject = { message , topic } ;
2020-09-01 08:40:18 -07:00
2024-06-24 09:51:59 -07:00
if ( options . onlyMessage ) {
//@ts-ignore
result = [ message ] ;
}
2023-08-16 04:06:47 -07:00
2024-06-24 09:51:59 -07:00
return [ this . helpers . returnJsonArray ( [ result ] ) ] ;
} ;
2020-09-01 08:40:18 -07:00
2024-06-24 09:51:59 -07:00
const manualTriggerFunction = async ( ) = >
await new Promise < void > ( async ( resolve ) = > {
client . once ( 'message' , ( topic , payload ) = > {
this . emit ( parsePayload ( topic , payload ) ) ;
resolve ( ) ;
2020-09-01 08:40:18 -07:00
} ) ;
2024-06-24 09:51:59 -07:00
await client . subscribeAsync ( topicsQoS ) ;
2020-09-01 08:40:18 -07:00
} ) ;
2021-04-30 18:23:25 -07:00
if ( this . getMode ( ) === 'trigger' ) {
2024-06-24 09:51:59 -07:00
const donePromise = ! options . parallelProcessing
? await this . helpers . createDeferredPromise < IRun > ( )
: undefined ;
client . on ( 'message' , async ( topic , payload ) = > {
this . emit ( parsePayload ( topic , payload ) , undefined , donePromise ) ;
await donePromise ? . promise ( ) ;
} ) ;
await client . subscribeAsync ( topicsQoS ) ;
2021-04-30 18:23:25 -07:00
}
2020-09-01 08:40:18 -07:00
async function closeFunction() {
2024-06-24 09:51:59 -07:00
await client . endAsync ( ) ;
2020-09-01 08:40:18 -07:00
}
return {
closeFunction ,
manualTriggerFunction ,
} ;
}
}