Skip to content

Commit 4cfde7d

Browse files
committed
Add suppport for MQTT message matching via RegExp
1 parent dfbf2cb commit 4cfde7d

3 files changed

Lines changed: 95 additions & 21 deletions

File tree

README.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,14 @@ Other users have been sharing configurations that work for them on our GitHub si
135135
### Camera MQTT Parameters
136136

137137
- `motionTopic`: The MQTT topic to watch for motion alerts.
138-
- `motionMessage`: The message to watch for to trigger motion alerts. Will use the name of the camera if blank.
138+
- `motionMessage`: The message to watch for to trigger motion alerts. Will use the name of the camera if both `motionMessage` and `motionMessageRegExp` are blank.
139+
- `motionMessageRegExp`: A RegExp string to match messages on the motion mqtt topic and trigger motion alerts. Ex: '".*"' will match any message.
139140
- `motionResetTopic`: The MQTT topic to watch for motion resets.
140-
- `motionResetMessage`: The message to watch for to trigger motion resets. Will use the name of the camera if blank.
141+
- `motionResetMessage`: The message to watch for to trigger motion resets. Will use the name of the camera if both `motionResetMessage` and `motionResetMessageRegExp` are blank.
142+
- `motionResetMessageRegExp`: A RegExp string to match messages on the motionReset mqtt topic and trigger motion alerts. Ex: '"/.*/"' will match any message.
141143
- `doorbellTopic`: The MQTT topic to watch for doorbell alerts.
142-
- `doorbellMessage`: The message to watch for to trigger doorbell alerts. Will use the name of the camera if blank.
144+
- `doorbellMessage`: The message to watch for to trigger doorbell alerts. Will use the name of the camera if both `doorbellMessage` and `doorbellMessageRegExp` are blank.
145+
- `doorbellMessageRegExp`: A RegExp string to match messages on the doorbell mqtt topic and trigger motion alerts. Ex: '".*"' will match any message.
143146

144147
#### Camera MQTT Example
145148

@@ -165,6 +168,30 @@ Other users have been sharing configurations that work for them on our GitHub si
165168
}
166169
```
167170

171+
#### Camera MQTT Example with RegExp Message Matching
172+
173+
```json
174+
{
175+
"platform": "Camera-ffmpeg",
176+
"cameras": [
177+
{
178+
"name": "Camera Name",
179+
"videoConfig": {
180+
"source": "-i rtsp://myfancy_rtsp_stream"
181+
},
182+
"mqtt": {
183+
"motionTopic": "home/camera",
184+
"motionMessageRegExp": "ON", // will match any message that contains "ON"
185+
"motionResetTopic": "home/camera",
186+
"motionResetMessageRegExp": "OFF", // will match any message that contains "OFF"
187+
"doorbellTopic": "home/doobell",
188+
"doorbellMessageRegExp": ".*" // will match all messages
189+
}
190+
}
191+
]
192+
}
193+
```
194+
168195
### Automation Parameters
169196

170197
- `mqtt`: Defines the hostname or IP of the MQTT broker to connect to for MQTT-based automation. If not set, MQTT support is not started. See the project site for [more information on using MQTT](https://sunoo.github.io/homebridge-camera-ffmpeg/automation/mqtt.html).

src/configTypes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@ export type VideoConfig = {
5454
export type MqttCameraConfig = {
5555
motionTopic?: string;
5656
motionMessage?: string;
57+
motionMessageRegExp?: string;
5758
motionResetTopic?: string;
5859
motionResetMessage?: string;
60+
motionResetMessageRegExp?: string;
5961
doorbellTopic?: string;
6062
doorbellMessage?: string;
63+
doorbellMessageRegExp?: string;
6164
};

src/index.ts

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import {
1111
PlatformAccessoryEvent,
1212
PlatformConfig
1313
} from 'homebridge';
14-
import http from 'http';
15-
import mqtt from 'mqtt';
16-
import { AutomationReturn } from './autoTypes';
1714
import { CameraConfig, FfmpegPlatformConfig } from './configTypes';
15+
16+
import { AutomationReturn } from './autoTypes';
1817
import { Logger } from './logger';
1918
import { StreamingDelegate } from './streamingDelegate';
19+
import http from 'http';
20+
import mqtt from 'mqtt';
21+
2022
const version = require('../package.json').version; // eslint-disable-line @typescript-eslint/no-var-requires
2123

2224
let hap: HAP;
@@ -40,7 +42,10 @@ class FfmpegPlatform implements DynamicPlatformPlugin {
4042
private readonly accessories: Array<PlatformAccessory> = [];
4143
private readonly motionTimers: Map<string, NodeJS.Timeout> = new Map();
4244
private readonly doorbellTimers: Map<string, NodeJS.Timeout> = new Map();
43-
private readonly mqttActions: Map<string, Map<string, Array<MqttAction>>> = new Map();
45+
private readonly mqttActions: Map<string, {
46+
messageMap: Map<string, Array<MqttAction>> | undefined,
47+
reMap: Map<RegExp, Array<MqttAction>> | undefined,
48+
}> = new Map();
4449

4550
constructor(log: Logging, config: PlatformConfig, api: API) {
4651
this.log = new Logger(log);
@@ -92,12 +97,22 @@ class FfmpegPlatform implements DynamicPlatformPlugin {
9297
api.on(APIEvent.DID_FINISH_LAUNCHING, this.didFinishLaunching.bind(this));
9398
}
9499

95-
addMqttAction(topic: string, message: string, details: MqttAction): void {
96-
const messageMap = this.mqttActions.get(topic) || new Map();
97-
const actionArray = messageMap.get(message) || [];
98-
actionArray.push(details);
99-
messageMap.set(message, actionArray);
100-
this.mqttActions.set(topic, messageMap);
100+
addMqttAction(topic: string, message: string | RegExp, details: MqttAction): void {
101+
let { messageMap, reMap } = this.mqttActions.get(topic) || { messageMap: undefined, reMap: undefined };
102+
let actionArray
103+
104+
if (typeof message === 'string') {
105+
actionArray = messageMap?.get(message) || []
106+
messageMap = messageMap || new Map()
107+
actionArray.push(details);
108+
messageMap.set(message, actionArray);
109+
} else { // RegExp
110+
actionArray = reMap?.get(message) || []
111+
reMap = reMap || new Map()
112+
actionArray.push(details);
113+
reMap.set(message, actionArray)
114+
}
115+
this.mqttActions.set(topic, {messageMap, reMap});
101116
}
102117

103118
setupAccessory(accessory: PlatformAccessory, cameraConfig: CameraConfig): void {
@@ -171,16 +186,32 @@ class FfmpegPlatform implements DynamicPlatformPlugin {
171186
if (this.config.mqtt) {
172187
if (cameraConfig.mqtt) {
173188
if (cameraConfig.mqtt.motionTopic) {
174-
this.addMqttAction(cameraConfig.mqtt.motionTopic, cameraConfig.mqtt.motionMessage || cameraConfig.name!,
175-
{accessory: accessory, active: true, doorbell: false});
189+
if (cameraConfig.mqtt.motionMessageRegExp) {
190+
this.addMqttAction(cameraConfig.mqtt.motionTopic, new RegExp(cameraConfig.mqtt.motionMessageRegExp),
191+
{accessory: accessory, active: true, doorbell: false});
192+
} else {
193+
this.addMqttAction(cameraConfig.mqtt.motionTopic, cameraConfig.mqtt.motionMessage || cameraConfig.name!,
194+
{accessory: accessory, active: true, doorbell: false});
195+
}
176196
}
177197
if (cameraConfig.mqtt.motionResetTopic) {
178-
this.addMqttAction(cameraConfig.mqtt.motionResetTopic, cameraConfig.mqtt.motionResetMessage || cameraConfig.name!,
179-
{accessory: accessory, active: false, doorbell: false});
198+
if (cameraConfig.mqtt.motionResetMessageRegExp) {
199+
this.addMqttAction(cameraConfig.mqtt.motionResetTopic, new RegExp(cameraConfig.mqtt.motionResetMessageRegExp),
200+
{accessory: accessory, active: false, doorbell: false});
201+
} else {
202+
this.addMqttAction(cameraConfig.mqtt.motionResetTopic, cameraConfig.mqtt.motionResetMessage || cameraConfig.name!,
203+
{accessory: accessory, active: false, doorbell: false});
204+
}
205+
180206
}
181207
if (cameraConfig.mqtt.doorbellTopic) {
182-
this.addMqttAction(cameraConfig.mqtt.doorbellTopic, cameraConfig.mqtt.doorbellMessage || cameraConfig.name!,
183-
{accessory: accessory, active: true, doorbell: true});
208+
if (cameraConfig.mqtt.doorbellMessageRegExp) {
209+
this.addMqttAction(cameraConfig.mqtt.doorbellTopic, new RegExp(cameraConfig.mqtt.doorbellMessageRegExp),
210+
{accessory: accessory, active: true, doorbell: true});
211+
} else {
212+
this.addMqttAction(cameraConfig.mqtt.doorbellTopic, cameraConfig.mqtt.doorbellMessage || cameraConfig.name!,
213+
{accessory: accessory, active: true, doorbell: true});
214+
}
184215
}
185216
}
186217
}
@@ -367,7 +398,7 @@ class FfmpegPlatform implements DynamicPlatformPlugin {
367398
}
368399
});
369400
client.on('message', (topic: string, message: Buffer) => {
370-
const messageMap = this.mqttActions.get(topic);
401+
const { messageMap, reMap } = this.mqttActions.get(topic) || { messageMap: undefined, reMap: undefined };
371402
if (messageMap) {
372403
const actionArray = messageMap.get(message.toString());
373404
if (actionArray) {
@@ -380,6 +411,19 @@ class FfmpegPlatform implements DynamicPlatformPlugin {
380411
}
381412
}
382413
}
414+
if (reMap) {
415+
for (const [re, actionArray] of reMap) {
416+
if (re.test(message.toString())) {
417+
for (const action of actionArray) {
418+
if (action.doorbell) {
419+
this.doorbellHandler(action.accessory, action.active);
420+
} else {
421+
this.motionHandler(action.accessory, action.active);
422+
}
423+
}
424+
}
425+
}
426+
}
383427
});
384428
}
385429
if (this.config.porthttp) {
@@ -421,4 +465,4 @@ export = (api: API): void => {
421465
Accessory = api.platformAccessory;
422466

423467
api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, FfmpegPlatform);
424-
};
468+
};

0 commit comments

Comments
 (0)