Hi!
A while ago I had some issues with the Mikrotik binding so I created a Wi-Fi-based (guest) presence solution with MikroTik (CAPsMAN and the IoT package) and MQTT.
Since RouterOS 7.22, it is possible to run a script on log events. I use CAPsMAN, but it might also work with regular Wi-Fi.
(disclaimer: I wrote this howto, AI spellchecked it :))
Requirements
- MikroTik router/access point with RouterOS 7.22+
- IoT package installed on MikroTik for MQTT functionality
- MQTT broker (I use Mosquitto)
- OpenHAB with MQTT and JavaScript rules configured
- Scripts
On MikroTik
- Install the IoT package and reboot. (Packages | RouterOS Manual)
- Configure the MQTT broker ( MQTT | RouterOS Manual )
/iot mqtt brokers
add address=1.2.3.4 keep-alive=30 name=Mosquitto password="SuperSecret" username=someIotUser
- Add a script that publishes all connected Wi-Fi clients to MQTT as a JSON string (the broker name should match the broker from step 2).
/system script
add dont-require-permissions=no name=PublishWifiMQTT owner=admin policy=\
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":local broker \"Mosquitto\"\r\
\n\r\
\n:local topic \"capsman/registrationtable\"\r\
\n\r\
\n/iot mqtt publish broker=\$broker topic=\$topic message=[/interface/wifi/registration-table/print proplist=mac-address,ssid as-\
value]\r\
\n"
- Add a logging action. This tells RouterOS what to do when a configured log event occurs (the name should match the script name from step 3) ( logging | RouterOS Manual ).
/system logging action
add name=WiFiEvent script=PublishWifiMQTT target=script
-
Add a log rule (the action name should match the action name from step 4).
It matches all log entries with the topic
wireless,infoand content containingconnectedordisconnected.
/system logging
add action=WiFiEvent regex="(dis)\?connected" topics=wireless,info
On OpenHAB
-
Create a Generic MQTT Thing.
Under Things, click +, select the MQTT binding, and choose Generic MQTT Thing.
Select your MQTT Bridge and click Create Thing.In the newly created Thing, under the Channels tab, click Add Channel. Pick an ID, label, and description. Select the channel type String. The MQTT state topic should match the topic set in the RouterOS script. Click Create.
Click on the newly created channel and select Add Link to Item. Choose Create a New Item and click Link.
version: 1
things:
mqtt:topic:8fc118bcb1:fe62d94935:
bridge: mqtt:broker:8fc118bcb1
label: CapsMan
channels:
RegistrationTable:
type: string
label: RegistrationTable
description: ""
config:
stateTopic: capsman/registrationtable
-
Create items for every device you want to monitor/track.
Under Items, click +, and choose an identifier, name, and label.
Select the type Switch.
Under Non-Semantic Tags, add WirelessClient.
Click Create.
In the newly created item, click Add Metadata. Select Enter Custom Namespace and enter
MacAddress. On the next screen, enter the MAC address in the value field:
value: AA:BB:CC:DD:EE:FF
config: {}
version: 1
items:
TomsS22Ultra_Online:
type: Switch
label: Toms S22 Ultra Online
icon: man_1
groups:
- Tom_present
tags:
- Point
- WirelessClient
metadata:
MacAddress: AA:BB:CC:DD:EE:FF
-
Create a group for every person you want to track.
In case a person owns multiple devices, a single connected device means that person is present.
Under Items, click +, and choose an identifier, name, and label.
Select the type Group.
Set Members Base Type to Switch.
Set Aggregation Function to One ON then ON else OFF.
In the newly created group, add all devices owned by that person from step 2.
version: 1
items:
Tom_present:
type: Group
group:
type: Switch
function: Or
parameters:
- "ON"
- "OFF"
label: Tom present
icon: man_1
-
Create a rule.
Under Rules, click + and enter a label such as
CapsManRegistrationTable.Under When, click Add Trigger, select Show All, and choose An item state changes. Select the item created in step 1.
Under Then, click Add Action, choose Inline Script, and select ECMAScript 262 Edition 11.
On the next screen, add the following script:
//console.log(event);
//console.log(event.eventName);
//console.log(event.payload);
switch(event.eventName){
case 'ExecutionEvent':
break;
case 'TimerEvent':
break;
case 'ItemCommandEvent':
/*
switch(event.itemName.toString()){
case '':
break;
}
*/
break;
case 'ItemStateChangedEvent':
switch(event.itemName.toString()){
case 'CapsMan_RegistrationTable':
var newValue = event.newState.toLocaleUpperCase();
var oldValue = event.oldState.toLocaleUpperCase();
/*
var guestCount = newValue.match(/GUEST/g)?.length;
if(guestCount > 0){
//items.getItem('GuestMode').sendCommandIfDifferent('ON');
items.getItem('Guest_count').sendCommandIfDifferent(guestCount);
items.getItem('Guest_present').sendCommandIfDifferent('ON');
}else{
items.getItem('Guest_count').sendCommandIfDifferent(0);
items.getItem('Guest_present').sendCommandIfDifferent('OFF');
}
*/
items.getItemsByTag('WirelessClient').forEach(function(i){
var metaData = i.getMetadata('MacAddress');
if(metaData){
if(newValue.match(metaData.value)){
i.sendCommandIfDifferent("ON");
}else{
i.sendCommandIfDifferent("OFF");
}
}else{
logger.warn('[CAPSMAN] Client ' + i.name + ' is missing mac address!');
}
});
var oldArray = Array.from(oldValue.matchAll("([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})"), (m) => m[0]);
var newArray = Array.from(newValue.matchAll("([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})"), (m) => m[0]);
var added = newArray.filter(x => !oldArray.includes(x));
var deleted = oldArray.filter(x => !newArray.includes(x));
if(added.length){
console.log("[CAPSMAN] Added: " + added)
}
if(deleted.length){
console.log("[CAPSMAN] Deleted: " + deleted)
}
break;
}
break;
case 'ItemStateUpdatedEvent':
break;
case 'ThingStatusInfoChangedEvent':
/*
switch(event.thingUID.toString()){
case '':
break;
}
*/
break;
}
For guests, I have a separate SSID named GUEST. In the commented-out section of the script, I check for clients connected to this SSID and update items accordingly.
Hopefully this is useful for someone looking to implement Wi-Fi-based presence detection with Mikrotik.