Foreward
The default MySensors MQTT implementation does not include a Last Will & Testament feature. As a result, I need to check ‘manually’ whether each sensor is still alive.
I’ve got what I need working - I’m just not particularly impressed with my implementation, so would be happy to get feedback.
Setup
I have:
- An Arduino Pro Mini with a DHT22 attached to it running a MySensors sketch.
- The Pro Mini sends data every minute to a MySensors MQTT gateway.
- The MySensors MQTT gateway publishes that data to my Mosquitto MQTT broker.
The MySensors gateway, Mosquitto and OpenHAB (2.5.5 Release Build) are all running on the same Raspberry Pi 3B+.
OpenHAB setup
mysensors.things
Thing mqtt:topic:msSpareRoom "Spare Room" (mqtt:broker:MosquittoMqttBroker) {
Channels:
Type number : temperature "Temperature" [
stateTopic="mysensors-out/2/1/1/0/0"
]
}
mysensors.items
Thanks to this clever shenanigans, I can use the fact that the Pro Mini sends out a regular temperature reading to create an item which stores the timestamp of the last message.
DateTime dtSpareRoomLastUpdate (gSensors) { channel="mqtt:topic:msSpareRoom:temperature" [profile="timestamp-update"]}
The item is part of the gSensors
group - my intention is to create more of these sensors, adding them to the group as I go. The value that is stored by this item looks as follows:
2020-07-16T21:22:55.677+0100
rules
Now here’s where it gets a little ugly, in my opinion. All I’m trying to do is check whether the last update that I’ve received has been within the last 5 minutes, and I’m triggering this by simply checking every 5 minutes using cron.
@rule("Sensor online", description="Check if sensors are still sending data")
@when("Time cron 0 0/5 0 ? * * *")
#@when("Item sTestSwitch changed to ON")
def action_sensor_status(event):
action_sensor_status.log.info("Checking if sensors are online")
update_interval = 5
for item in ir.getItem("gSensors").members:
raw_item_name = str(item.name)
if(is_alive(str(item.state), update_interval)):
#Sensor online
action_sensor_status.log.info(raw_item_name + " is online")
else:
#Sensor offline
action_sensor_status.log.info(raw_item_name + " is offline")
def is_alive(last_change, minutes):
utc_time = datetime.strptime(last_change[:-5], "%Y-%m-%dT%H:%M:%S.%f")
epoch_time = (utc_time - datetime(1970, 1, 1)).total_seconds()
current_time = (datetime.now() - datetime(1970, 1, 1)).total_seconds()
if(current_time-epoch_time > (60*minutes)):
#Sensor offline
return False
else:
#Sensor online
return True
I find the is_alive
function ugly, with all the massaging of the dates into something useful. What I’m trying to do is convert the timestamp into seconds since the epoch, and compare that to the current seconds since the epoch. I was quite surprise that I had to do all the above just to get that to work (I understand that this should be easier with Python3, but we don’t have that yet in native openHAB).
The ugliest is having to strip the timezone offset from the timestamp ([:-5]
) in order to work with strptime
. A long time back I used to do quite a bit of PHP, which had a stringtotime() function into which you could throw anything and it would spit out a timestamp - I couldn’t find the same in Python2.7.
I did have a look at using openHAB’s date and time methods, but 1) I got confused very quickly and 2) I really quite like the idea of using native python for as much as possible.
So that’s it basically - what have I missed? What glaringly obvious thing should I have done instead of the spaghetti shown above? Thanks for reading to the end!