This was the solution, and it makes sense. Using a key/value pair to identify and ‘hold’ a timer is clever! I now also known what a python dictionary is…!
For completeness, here is the code from the OP modified so it now works:
Rule
import personal.personal_functions
reload(personal.personal_functions)
from personal.personal_functions import toggle_dimming
...
toggle_dimming(dimmer_item_name, t)
personal_functions.js
from core.jsr223.scope import ir
from core.jsr223.scope import events
from threading import Timer
...
#Initialise a timer dict
my_timers = {}
...
def toggle_dimming(item_name, interval):
if my_timers.get(item_name) is None or not my_timers[item_name].isAlive():
# A timer does not exist, so create one
start_dimming(item_name, interval)
else:
stop_dimming(item_name)
...
def start_dimming(item_name, interval):
#Grab current dimming level
current_dimmer_level = int(str(ir.getItem(item_name).state))
if current_dimmer_level is not 0:
#Adjust dimmer
events.sendCommand(ir.getItem(item_name), str(current_dimmer_level-1))
#Start timer
my_timers[item_name] = Timer(interval, start_dimming, [item_name, interval])
my_timers[item_name].start()
else:
#Dimming finished
my_timers[item_name] = None
...
def stop_dimming(item_name):
if my_timers.get(item_name) is not None and my_timers[item_name].isAlive():
my_timers[item_name].cancel()