Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If Rule is edited, get multiple instances of rule running #80

Closed
NickWaterton opened this issue Oct 9, 2019 · 18 comments
Closed

If Rule is edited, get multiple instances of rule running #80

NickWaterton opened this issue Oct 9, 2019 · 18 comments

Comments

@NickWaterton
Copy link

I may be wrong, but it seems if I edit a rule, and save it, then I end up with multiple instances of the rule running.

If i restart HABApp, then everything is OK.

I noticed this, when I'm using

self.listen_event('HEM_P', self.item_value_changed, ValueChangeEvent)

I edited and saved the rule, then I started getting two messages in the log file, for the same event.

It doesn't do it every time, only if I make a major change in the file (like delete an rule, or add a listener).

Also, is there any difference between ItemStateChangedEvent and ValueChangeEvent? I seem to get two triggers for ValueChangeEvent sometimes, but only one for ItemStateChangedEvent (but this could just be part of the same issue described above).

I'm still learning how to use your library, so I'm revising rules a lot, and this seems to become an issue (fixed by a restart so not a huge deal).

I have three rules files currently, could this be part of the issue?

Thanks,

@NickWaterton NickWaterton changed the title If Rule is edited, get multiple instances or rule running If Rule is edited, get multiple instances of rule running Oct 9, 2019
@NickWaterton
Copy link
Author

NickWaterton commented Oct 9, 2019

Here is some more info:

I had an error in a rule. I fixed the error, saved the rule file, and tested again. This is what I got:

[2019-10-09 11:38:19,209] [         MyRule.SyncLights]     INFO |        item_command: Item basementPanelFront received command 34.0%
[2019-10-09 11:38:19,210] [         MyRule.SyncLights]     INFO |        item_command: name:basementPanelBack value:0
[2019-10-09 11:38:19,210] [         MyRule.SyncLights]    ERROR |        item_command: '>' not supported between instances of 'int' and 'str'
[2019-10-09 11:38:19,213] [         MyRule.SyncLights]     INFO |        item_command: Item basementPanelFront received command 34.0%
[2019-10-09 11:38:19,216] [         MyRule.SyncLights]     INFO |        item_command: basementPanelFront value: 34
[2019-10-09 11:38:19,216] [         MyRule.SyncLights]     INFO |        item_command: setting basementFluorescents to ON

You can see that I set basementPanelFront to 34%. I get two item_command triggers, the first is from the instance of the rule with the error which is still running, the second trigger is from a second instance of the same rule, which is fixed.

After restarting HABApp, this goes away:

[2019-10-09 11:43:12,035] [         MyRule.SyncLights]     INFO |        item_command: Item basementPanelFront received command 34.0%
[2019-10-09 11:43:12,041] [         MyRule.SyncLights]     INFO |        item_command: basementPanelFront value: 34
[2019-10-09 11:43:12,041] [         MyRule.SyncLights]     INFO |        item_command: setting basementFluorescents to ON

What this means is that I have to remember to restart HABApp every time i change a rule, otherwise I get the old and new behavior (sometimes), and if it's a bug, the bug still happens (even though it's fixed), because the old rule is still running - which is confusing to say the least.

This mostly seems to happen after an error in a rule.

I assume this is not what is intended to happen?

@spacemanspiff2007
Copy link
Owner

ItemStateChangedEvent is the event from openhab, ValueChangeEvent is the event that occurs when an item has changed in HABApp. Both are interchangeable in this context and there should not be a different behavior.

Do you see in the HABApp.log that the (old) rule gets properly unloaded? Are you somehow caching the rule instance?

@NickWaterton
Copy link
Author

It's a bit hard to tell if the rule gets unloaded, as when you restart HABApp, it deletes the log file.

I'll let you know if I figure it out.

@spacemanspiff2007
Copy link
Owner

Do you observe the same issue with 0.10?
The logs should get rotated there instead of deleted.

@NickWaterton
Copy link
Author

NickWaterton commented Nov 2, 2019 via email

@spacemanspiff2007
Copy link
Owner

Could you provide a (faulty) rule so I can reproduce it?

@NickWaterton
Copy link
Author

Here is a (filtered) log for my miniRemote rule. The top section is normal. If I make no changes to the rule, and save it again, the lower section is what I get:

[2019-11-02 10:35:40,315] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item miniRemoteBackGarden2ButtonA received update ON
[2019-11-02 10:35:40,316] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(ON)
[2019-11-02 10:35:40,317] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: ON, state: 0
[2019-11-02 10:35:40,318] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: setting lamp to : <Color hue: 60.0°, saturation: 100.0%, brightness: 100.0%>
[2019-11-02 10:35:47,839] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item miniRemoteBackGarden2ButtonA received update OFF
[2019-11-02 10:35:47,840] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(OFF)
[2019-11-02 10:35:47,840] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: OFF, state: 1


[2019-11-02 10:36:07,199] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item miniRemoteBackGarden2ButtonA received update ON
[2019-11-02 10:36:07,199] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(ON)
[2019-11-02 10:36:07,199] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-02 10:36:07,201] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: ON, state: 0
[2019-11-02 10:36:07,203] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-02 10:36:07,204] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(ON)
[2019-11-02 10:36:07,205] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: setting lamp to : <Color hue: 60.0°, saturation: 100.0%, brightness: 100.0%>
[2019-11-02 10:36:07,206] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(ON)
[2019-11-02 10:36:07,208] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: ON, state: 0
[2019-11-02 10:36:07,212] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: ON, state: 0
[2019-11-02 10:36:07,213] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: setting lamp to : <Color hue: 60.0°, saturation: 100.0%, brightness: 100.0%>
[2019-11-02 10:36:07,214] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: setting lamp to : <Color hue: 60.0°, saturation: 100.0%, brightness: 100.0%>
[2019-11-02 10:36:30,617] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item miniRemoteBackGarden2ButtonA received update OFF
[2019-11-02 10:36:30,618] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(OFF)
[2019-11-02 10:36:30,618] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item ButtonA received update OFF
[2019-11-02 10:36:30,619] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item ButtonA received update OFF
[2019-11-02 10:36:30,620] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: OFF, state: 1
[2019-11-02 10:36:30,622] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(OFF)
[2019-11-02 10:36:30,624] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(OFF)
[2019-11-02 10:36:30,628] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: OFF, state: 1
[2019-11-02 10:36:30,628] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: OFF, state: 1

I've attached the HABapp.log file.

The rule is this:

import HABApp
from HABApp.core import WrappedFunction
from HABApp.core.events import ValueUpdateEvent, ValueChangeEvent, ValueNoChangeEvent, ValueNoUpdateEvent
from HABApp.openhab.events import ItemStateEvent, ItemCommandEvent, ItemStateChangedEvent, GroupItemStateChangedEvent
from HABApp.openhab.items import SwitchItem, ContactItem, RollershutterItem, DimmerItem, NumberItem
from HABApp.core.items import Item, ColorItem
from HABApp.openhab.definitions.values import OnOffValue, PercentValue
from HABApp.mqtt.items import MqttItem
import asyncio

import sys, time, datetime
import logging
import importlib
import json

from threading import Timer,Thread,Event
import utils
from utils import *

class miniRemote(HABApp.Rule):
    '''
    mini remotes triggers
    format of mini remote command items is:
    ON/OFF event:
    'miniRemote' remote name 'Button' letter
    append 'F' for faston event
    append 'D' for dimmer event
    eg
    miniRemoteFamilyButtonC
    miniRemoteFamilyButtonAF
    miniRemoteFamilyButtonBD
    '''
    
    watch_items =  ['ButtonA',
                    'ButtonB',
                    'ButtonC',
                    'ButtonD',
                    'ButtonE',
                    'ButtonF',
                    'ButtonG',
                    'ButtonH',
                    'ButtonAF',
                    'ButtonBF',
                    'ButtonCF',
                    'ButtonDF',
                    'ButtonEF',
                    'ButtonFF',
                    'ButtonGF',
                    'ButtonHF']
    change_items = ['ButtonAD',
                    'ButtonBD',
                    'ButtonCD',
                    'ButtonDD',
                    'ButtonED',
                    'ButtonFD',
                    'ButtonGD',
                    'ButtonHD']
    command_item = []
    items =        []
    
                    
    def __init__(self, remote, *targets):
        super().__init__()
        self.log = logging.getLogger('MyRule.'+self.__class__.__name__)
        
        self.remote = remote
        self.prefix = 'miniRemote'
        self.parent = None
        
        self.items = [target[1] for target in targets]
        self.targets = {target[0]: target[1] for target in targets}
        
        self.item = {}
        for item in self.watch_items:
            item = self.prefix + self.remote + item
            try:
                self.item[item] = Item.get_item(item)
                self.listen_event(self.item[item], self.update_received, ItemStateEvent)
            except HABApp.core.Items.ItemNotFoundException:
                pass
            
        for item in self.change_items:
            item = self.prefix + self.remote + item
            try:
                self.item[item] = Item.get_item(item)
                self.listen_event(self.item[item], self.change_received, ItemStateChangedEvent)
            except HABApp.core.Items.ItemNotFoundException:
                pass
  
        for item in self.command_item:
            item = self.prefix + self.remote + item
            try:
                self.item[item] = Item.get_item(item)
                self.listen_event(self.item[item], self.command_received, ItemCommandEvent)
            except HABApp.core.Items.ItemNotFoundException:
                pass
            
        for item in self.items:
            try:
                self.item[item] = Item.get_item(item)
            except HABApp.core.Items.ItemNotFoundException:
                pass 
            
    def command_received(self, event):
        self.log.debug('Item %s command received %s' % (event.name, event.value))
        
        event.name = event.name.replace(self.prefix+self.remote, '')
        
        self.exectute_command(event)
        
    def update_received(self, event):
        self.log.debug('Item %s received update %s' % (event.name, event.value))
        
        event.name = event.name.replace(self.prefix+self.remote, '')
        
        self.exectute_command(event)
        
    def change_received(self, event):
        self.log.debug('Item %s changed to %s' % (event.name, event.value))

        event.name = event.name.replace(self.prefix+self.remote, '')
        
        self.exectute_command(event)
        
    def exectute_command(self, event):
        button = event.name.replace('Button', '')
        if button in self.targets.keys():
            target = self.targets[button]
            if target in self.item.keys():
                self.log.info('%s%s sending command %s to item %s' % (self.prefix, self.remote, event.value, self.item[target].name))
                self.oh.send_command(self.item[target], event.value)
            else:
                if self.parent is None:
                    func = globals().get(target, None)
                else:
                    func = getattr(self.parent, target)
                if func is not None:
                    self.log.info('%s executing function %s(%s)' % (self.remote, target, event.value))
                    func(event.value)
                else:
                    self.log.info('%s no function %s() found' % (self.remote, target))
        else:
            self.log.info('No Action for remote: %s, Button: %s' % (self.remote, button))
            
            
            
class miniRemote_BackGarden(miniRemote):

    def __init__(self, remote, *targets):
        super().__init__(remote, *targets)
        self.parent = self
        
        #Aeon RGB bulb control (deck lamp)
        # there are really only 3 controls, dimmer, colour and colour temperature
        self.lamp_switch = Item.get_item('AeonRGBBulbSwitch')    #Deck Lamp On/Off
        self.lamp_dimmer = Item.get_item('AeonRGBBulbDimmer')    #Deck Lamp Brightness
        self.lamp_colour = Item.get_item('AeonRGBBulbColour')    #Deck Lamp Colour item (HSB)
        self.lamp_colour_dimmer = Item.get_item('AeonRGBBulbColourDimmer')    #Deck Lamp Colour item (dimmer)
        self.lamp_colour_dimmer_Homekit = Item.get_item('AeonRGBBulbColourDimmer_Homekit')    #Deck Lamp Colour item (dummy dimmer)
        self.lamp_colour_switch = Item.get_item('AeonRGBBulbColourSwitch')    #Deck Lamp Colour item (switch)
        self.lamp_colour_temp = Item.get_item('AeonRGBBulbColour_Temp')    #Deck Lamp Colour temperature item (dimmer)
        self.lamp_colour_temp_switch = Item.get_item('AeonRGBBulbColour_Temp_switch')    #Deck Lamp Colour temperature item (switch)
        #Note Warm white has the highest priority, when set to 100, RGB can't be used. set to 0 to allow cold white/RGB
        self.deck_lamp_state = 0

        
    def deckLamp(self, value):
        self.log.info('executing deckLamp, with value: %s, state: %s' % (value, self.deck_lamp_state))
        if value == 'ON':
            if self.deck_lamp_state == 0:
                #self.oh.send_command(self.lamp_colour_temp, 0)    #cold white/rgb
                self.lamp_colour.set_rgb(255, 255, 0)   #yellow
                self.log.info('setting lamp to : %s' % repr(self.lamp_colour))
                self.oh.send_command(self.lamp_colour, (60, 100, 100))  #100% yellow
                self.oh.send_command(self.lamp_switch, 'ON')
                self.deck_lamp_state = 1
                
            elif self.deck_lamp_state == 1:
                self.oh.send_command(self.lamp_colour_temp, 100) #warm white
                self.oh.send_command(self.lamp_switch, 'ON')
                self.deck_lamp_state = 2
                
            elif self.deck_lamp_state == 2:
                self.oh.send_command(self.lamp_colour_temp, 0)    #cold white/rgb
                self.oh.send_command(self.lamp_switch, 'ON')
                self.deck_lamp_state = 0

        else:
            self.oh.send_command(self.lamp_switch, 'OFF')
            self.deck_lamp_state = 0
            
    def decklampDimmer(self, value):
        #Deck Lamp Brighten/dim
        self.log.info('executing decklampDimmer, with value: %s' % value)
        #ToDo
            
    def fenceLights(self, value):
        #Fence Lights on/off
        self.log.info('executing fenceLights, with value: %s' % value)
        if value == 'ON':
            self.oh.send_command('DIRECTsequence', "8")         #fence lights fadeHSI
        else:
            sself.oh.send_command('DIRECTsequence', "2")        #fence lights OFF
 
    def deckLights(self, value):
        #deck Lights on/off
        self.log.info('executing fenceLights, with value: %s' % value)
        if value == 'ON':
            if self.get_state('RGBW_Deck_Enable') == 'ON':    #if backup controller is enabled{
                sself.oh.send_command('RGBWControllerR_Deck', 100)    #deck lights ON
                sself.oh.send_command('RGBWControllerG_Deck', 100)    #deck lights ON
                sself.oh.send_command('RGBWControllerB_Deck', 100)    #deck lights ON
                sself.oh.send_command('RGBWControllerW_Deck', 100)    #deck step lights ON

            if self.get_state('RGBW_Deck_Enable') == 'OFF':           #if primary controller is enabled
                sself.oh.send_command('RGBSAll_Deck_MQTT', 'ON')      #All deck lights ON

        else:
            if self.get_state('RGBW_Deck_Enable') == 'ON':    #if backup controller is enabled{
                sself.oh.send_command('RGBWControllerR_Deck', 0)      #deck lights OFF
                sself.oh.send_command('RGBWControllerG_Deck', 0)      #deck lights OFF
                sself.oh.send_command('RGBWControllerB_Deck', 0)      #deck lights OFF
                sself.oh.send_command('RGBWControllerW_Deck', 0)      #deck step lights OFF

            if self.get_state('RGBW_Deck_Enable') == 'OFF':          #if primary controller is enabled
                sself.oh.send_command('RGBSAll_Deck_MQTT', 'OFF')     #ll deck lights OF
                        
    def awning(self, value):
        #Awning control
        self.log.info('executing awning, with value: %s' % value)
        if value == 'OFF':
            self.log.info("Awning - Sending Command DOWN")
            sself.oh.send_command('awningPosition', 'DOWN')
        else:
            self.log.info("Awning - Sending Command UP")
            sself.oh.send_command('awningPosition', 'UP')
        
    def awningStop(self, value):
        #Awning Stop
        self.log.info('executing awningStop, with value: %s' % value)
        sself.oh.send_command('awningPosition', 'STOP')

    def awningPosition(self, value):
        #Awning Continuous in/out/stop
        self.log.info('executing awningPosition, with value: %s' % value)
        if value == 0:  #note for ISY this is -1
            self.log.info("Awning - Sending Command DOWN")
            sself.oh.send_command('awningPosition', 'DOWN')
        elif value == 1:  #note for ISY this is 0
            self.log.info("Awning - Sending CommandSTOP")
            sself.oh.send_command('awningPosition', 'STOP')
        elif value == 2:  #note for ISY this is 1
            self.log.info("Awning - Sending Command UP")
            sself.oh.send_command('awningPosition', 'UP')
        

miniRemote('Family', ('C', 'frontDoorLock'))
miniRemote('Basement', ('D', 'workbenchLight'))
miniRemote_BackGarden('BackGarden2',
                      ('A', 'deckLamp'),
                      ('AF', 'deckHeater'),
                      ('B', 'decklampDimmer'),
                      ('BD', 'decklampDimmer'),
                      ('BF', 'deckFan'),
                      ('C', 'fenceLights'),
                      ('CF', 'deckLights'),
                      ('D', 'awning'),
                      ('DF', 'awningStop'),
                      ('DD', 'awningPosition'))
miniRemote_BackGarden('BackGarden',
                      ('A', 'deckLamp'),
                      ('AF', 'deckHeater'),
                      ('B', 'decklampDimmer'),
                      ('BD', 'decklampDimmer'),
                      ('BF', 'deckFan'),
                      ('C', 'fenceLights'),
                      ('CF', 'deckLights'),
                      ('D', 'awning'),
                      ('DF', 'awningStop'),
                      ('DD', 'awningPosition'))

This is a new rule file I just created yesterday, so it's WIP, there are a lot of things i can simplify, I'm just getting it working, before I prettify it. This involves a lot of changes, and I have to restart HABApp every time i save the file.

Any Idea why it duplicates all the time?
HABApp.log

@spacemanspiff2007
Copy link
Owner

I tried to reproduce it but failed because I am missing most of the item configurations.
However I can see that the old rules get properly unloaded, so it is definatly a problem with your rule.

If you take a look at the provided log:

[2019-11-02 10:36:07,199] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item miniRemoteBackGarden2ButtonA received update ON
[2019-11-02 10:36:07,199] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(ON)
[2019-11-02 10:36:07,199] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-02 10:36:07,201] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: ON, state: 0
[2019-11-02 10:36:07,203] [MyRule.miniRemote_BackGarden]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-02 10:36:07,204] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(ON)
[2019-11-02 10:36:07,205] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: setting lamp to : <Color hue: 60.0°, saturation: 100.0%, brightness: 100.0%>
[2019-11-02 10:36:07,206] [MyRule.miniRemote_BackGarden] INFO|    exectute_command: BackGarden2 executing function deckLamp(ON)
[2019-11-02 10:36:07,208] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: ON, state: 0
[2019-11-02 10:36:07,212] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: executing deckLamp, with value: ON, state: 0
[2019-11-02 10:36:07,213] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: setting lamp to : <Color hue: 60.0°, saturation: 100.0%, brightness: 100.0%>
[2019-11-02 10:36:07,214] [MyRule.miniRemote_BackGarden] INFO|            deckLamp: setting lamp to : <Color hue: 60.0°, saturation: 100.0%, brightness: 100.0%>

The first line is the event trigger and it only happens once which means that from the HABApp triggers everything is ok.
However this:

class miniRemote_BackGarden(miniRemote):

    def __init__(self, remote, *targets):
        super().__init__(remote, *targets)
        self.parent = self   # <--

and this:

                if self.parent is None:
                    func = globals().get(target, None) # <--
                else:
                    func = getattr(self.parent, target) # <--

shows that you are doing things fundamentally wrong and one of these parts is propably the cause of the error. There is never a reason to use globals() in a rule and the documentation explicitly warns that caching rule instances will cause problems.

I'm pretty sure if you restructure your code properly the problems will go away.

@NickWaterton
Copy link
Author

This is not the only rule that has this issue, this rule was just the one I had created that day.

Why would I not need to use globals()? I'm not actually using it at the moment, it's just a hook in case I need to access a global function.

self.parent is simply passing the instance of the sub class to the parent class - what's inherently wrong with that?

I have reduced the rule to this:

class miniRemote(HABApp.Rule):
    '''
    mini remotes triggers
    format of mini remote command items is:
    ON/OFF event:
    'miniRemote' remote name 'Button' letter
    append 'F' for faston event
    append 'D' for dimmer event
    eg
    miniRemoteFamilyButtonC
    miniRemoteFamilyButtonAF
    miniRemoteFamilyButtonBD
    '''
    
    watch_items =  ['ButtonA',
                    'ButtonB',
                    'ButtonC',
                    'ButtonD',
                    'ButtonE',
                    'ButtonF',
                    'ButtonG',
                    'ButtonH',
                    'ButtonAF',
                    'ButtonBF',
                    'ButtonCF',
                    'ButtonDF',
                    'ButtonEF',
                    'ButtonFF',
                    'ButtonGF',
                    'ButtonHF']
    change_items = ['ButtonAD',
                    'ButtonBD',
                    'ButtonCD',
                    'ButtonDD',
                    'ButtonED',
                    'ButtonFD',
                    'ButtonGD',
                    'ButtonHD']
    command_item = []
    items =        []
    
    dim =          {'UP': 1, 'STOP': 0, 'DOWN': -1} #isy values
    #dim =          {'UP': 2, 'STOP': 1, 'DOWN': 0}  #OH2 values
    
                    
    def __init__(self, remote, *targets):
        super().__init__()
        self.log = logging.getLogger('MyRule.'+self.__class__.__name__)
        
        self.remote = remote
        self.prefix = 'miniRemote'
        self.parent = None
        
        self.items = [target[1] for target in targets]
        self.targets = {target[0]: target[1] for target in targets}
        
        self.item = {}
        for item in self.watch_items:
            item = self.prefix + self.remote + item
            try:
                self.item[item] = Item.get_item(item)
                self.listen_event(self.item[item], self.update_received, ItemStateEvent)
            except HABApp.core.Items.ItemNotFoundException:
                pass
            
        for item in self.change_items:
            item = self.prefix + self.remote + item
            try:
                self.item[item] = Item.get_item(item)
                self.listen_event(self.item[item], self.change_received, ItemStateChangedEvent)
            except HABApp.core.Items.ItemNotFoundException:
                pass
  
        for item in self.command_item:
            item = self.prefix + self.remote + item
            try:
                self.item[item] = Item.get_item(item)
                self.listen_event(self.item[item], self.command_received, ItemCommandEvent)
            except HABApp.core.Items.ItemNotFoundException:
                pass
            
        for item in self.items:
            try:
                self.item[item] = Item.get_item(item)
            except HABApp.core.Items.ItemNotFoundException:
                pass 
            
    def command_received(self, event):
        self.log.debug('Item %s command received %s' % (event.name, event.value))
        
        event.name = event.name.replace(self.prefix+self.remote, '')
        self.exectute_command(event)
        
    def update_received(self, event):
        self.log.debug('Item %s received update %s' % (event.name, event.value))
        
        event.name = event.name.replace(self.prefix+self.remote, '')
        self.exectute_command(event)
        
    def change_received(self, event):
        self.log.debug('Item %s changed to %s' % (event.name, event.value))

        event.name = event.name.replace(self.prefix+self.remote, '')
        self.exectute_command(event)
        
    def exectute_command(self, event):
        button = event.name.replace('Button', '')
        if button in self.targets.keys():
            target = self.targets[button]
            if target in self.item.keys():
                self.log.info('%s%s sending command %s to item %s' % (self.prefix, self.remote, event.value, self.item[target].name))
                self.oh.send_command(self.item[target], event.value)
            else:
                if self.parent is None:
                    func = None
                else:
                    func = getattr(self.parent, target)
                if func is not None:
                    self.log.info('%s executing function %s(%s)' % (self.remote, target, event.value))
                    func(event.value)
                else:
                    self.log.info('%s no function %s() found' % (self.remote, target))
        else:
            self.log.info('No Action for remote: %s, Button: %s' % (self.remote, button))

miniRemote('Family', ('C', 'frontDoorLock'))
miniRemote('Basement', ('D', 'workbenchLight'))
miniRemote('Kitchen')

For testing, and it still duplicates the actions:
I have experimented, and I get this:

[2019-11-06 09:05:45,277] [         MyRule.miniRemote]DEBUG|     update_received: Item miniRemoteFamilyButtonA received update ON
[2019-11-06 09:05:45,277] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:05:45,277] [         MyRule.miniRemote]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-06 09:05:45,277] [         MyRule.miniRemote]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-06 09:05:45,278] [         MyRule.miniRemote]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-06 09:05:45,278] [         MyRule.miniRemote]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-06 09:05:45,278] [         MyRule.miniRemote]DEBUG|     update_received: Item ButtonA received update ON
[2019-11-06 09:05:45,279] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:05:45,280] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:05:45,281] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:05:45,282] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:05:45,283] [         MyRule.miniRemote] INFO|    exectute_command: No 1 Action for remote: Family, Button: A

Where I change the output text, save and test again. The output textt changes in the last line:

[2019-11-06 09:05:45,283] [         MyRule.miniRemote] INFO|    exectute_command: No 1 Action for remote: Family, Button: A

But not in the previous lines, so it seems that some previous instances of the rule are still running. This changes from test to test, sometimes I get 3 outputs, sometimes I get 5, sometimes 3 of them change, and the rest don't. Example:

[2019-11-06 09:30:07,979] [         MyRule.miniRemote]DEBUG|     update_received: Item miniRemoteFamilyButtonA received update OFF
[2019-11-06 09:30:07,981] [         MyRule.miniRemote]DEBUG|     update_received: Item miniRemoteFamilyButtonA received update OFF
[2019-11-06 09:30:07,982] [         MyRule.miniRemote]DEBUG|     update_received: Item miniRemoteFamilyButtonA received update OFF
[2019-11-06 09:30:07,982] [         MyRule.miniRemote]DEBUG|     update_received: Item miniRemoteFamilyButtonA received update OFF
[2019-11-06 09:30:07,982] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:30:07,983] [         MyRule.miniRemote]DEBUG|     update_received: Item ButtonA received update OFF
[2019-11-06 09:30:07,984] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:30:07,987] [         MyRule.miniRemote] INFO|    exectute_command: No Action for remote: Family, Button: A
[2019-11-06 09:30:07,988] [         MyRule.miniRemote] INFO|    exectute_command: No **TEST** Action for remote: Family, Button: A
[2019-11-06 09:30:07,993] [         MyRule.miniRemote] INFO|    exectute_command: No **TEST** Action for remote: Family, Button: A

Looking at the log, I can see where the file is unloaded, but then it gets reloaded 4 times. is that normal?

[2019-11-06 09:18:59,789] [              HABApp.Rules]DEBUG|              unload: File /etc/openhab2/HABApp/rules/mini_remotes.py successfully unloaded!
[2019-11-06 09:18:59,790] [              HABApp.Rules]DEBUG|   request_file_load: Loading file: /etc/openhab2/HABApp/rules/mini_remotes.py
.
..
[2019-11-06 09:18:59,820] [              HABApp.Rules] INFO|                load: Added rule "miniRemote" from mini_remotes.py
[2019-11-06 09:18:59,820] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.2" from mini_remotes.py
[2019-11-06 09:18:59,820] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.3" from mini_remotes.py
[2019-11-06 09:18:59,821] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden" from mini_remotes.py
[2019-11-06 09:18:59,821] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden.2" from mini_remotes.py
[2019-11-06 09:18:59,821] [              HABApp.Rules]DEBUG|   request_file_load: File /etc/openhab2/HABApp/rules/mini_remotes.py successfully loaded!
[2019-11-06 09:18:59,822] [              HABApp.Rules]DEBUG|   request_file_load: Loading file: /etc/openhab2/HABApp/rules/mini_remotes.py
.
.
[2019-11-06 09:18:59,850] [              HABApp.Rules] INFO|                load: Added rule "miniRemote" from mini_remotes.py
[2019-11-06 09:18:59,850] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.2" from mini_remotes.py
[2019-11-06 09:18:59,850] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.3" from mini_remotes.py
[2019-11-06 09:18:59,851] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden" from mini_remotes.py
[2019-11-06 09:18:59,851] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden.2" from mini_remotes.py
[2019-11-06 09:18:59,851] [              HABApp.Rules]DEBUG|   request_file_load: File /etc/openhab2/HABApp/rules/mini_remotes.py successfully loaded!
[2019-11-06 09:18:59,851] [              HABApp.Rules]DEBUG|   request_file_load: Loading file: /etc/openhab2/HABApp/rules/mini_remotes.py
.
.
[2019-11-06 09:18:59,904] [              HABApp.Rules] INFO|                load: Added rule "miniRemote" from mini_remotes.py
[2019-11-06 09:18:59,904] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.2" from mini_remotes.py
[2019-11-06 09:18:59,905] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.3" from mini_remotes.py
[2019-11-06 09:18:59,905] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden" from mini_remotes.py
[2019-11-06 09:18:59,906] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden.2" from mini_remotes.py
[2019-11-06 09:18:59,906] [              HABApp.Rules]DEBUG|   request_file_load: File /etc/openhab2/HABApp/rules/mini_remotes.py successfully loaded!
[2019-11-06 09:18:59,907] [              HABApp.Rules]DEBUG|   request_file_load: Loading file: /etc/openhab2/HABApp/rules/mini_remotes.py
.
.
[2019-11-06 09:18:59,986] [              HABApp.Rules] INFO|                load: Added rule "miniRemote" from mini_remotes.py
[2019-11-06 09:18:59,987] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.2" from mini_remotes.py
[2019-11-06 09:18:59,987] [              HABApp.Rules] INFO|                load: Added rule "miniRemote.3" from mini_remotes.py
[2019-11-06 09:18:59,987] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden" from mini_remotes.py
[2019-11-06 09:18:59,987] [              HABApp.Rules] INFO|                load: Added rule "miniRemote_BackGarden.2" from mini_remotes.py
[2019-11-06 09:18:59,988] [              HABApp.Rules]DEBUG|   request_file_load: File /etc/openhab2/HABApp/rules/mini_remotes.py successfully loaded!

The rule is stripped down to the minimum, not sub classed, not passing instances, not using globals(). Can you explain specifically what you think is wrong with this rule that causes this behavior? because I still think there is something else going on.

@spacemanspiff2007
Copy link
Owner

Just some general hints:
Use seperate logfiles for seperate rules files or at least group logs by functionality. This makes the logs much more readable. You can easily define new logfiles in the logging.yml under loggers and handlers.

Also this

        for item in self.watch_items:
            item = self.prefix + self.remote + item
            try:
                self.item[item] = Item.get_item(item)
                self.listen_event(self.item[item], self.update_received, ItemStateEvent)
            except HABApp.core.Items.ItemNotFoundException:
                pass

can be changed to this (just so there is increased readablity and it distinguishes between item and item names)

        for button in self.watch_items:
            item_name = self.prefix + self.remote + button
            self.item[item_name] = Item.get_create_item(item_name)
            self.listen_event(item_name, self.update_received, ItemStateEvent)

Anyway:

Looking at the log, I can see where the file is unloaded, but then it gets reloaded 4 times. is that normal?

No - this is not normal. I am missing the unload section since if the rule is loaded it should also be unloaded. Is this not the case?

Could you add the following rule in a new rule file?

import logging
from threading import Lock


log = logging.getLogger('HABApp.AllRules')
lock = Lock()

class LogAllRules(HABApp.Rule):

    def __init__(self):
        super().__init__()
        self.run_soon(self.show_rules)

    def show_rules(self):
        with lock:
            log.info('-' * 120)
            for r in self.get_rule(None):
                log.info(f'{r.rule_name}')
LogAllRules()

It will log the names of all running rules. If the rules are still running it should be easy to spot duplicate names. Can you show the output?

@NickWaterton
Copy link
Author

Thanks for the logging comments, but if you notice the name of the rule is included in the log, so I just filter by tailing the log through grep, that way I can filter on one or more rules to see what is happening..

Here is the output of your logging rule when HABApp is restarted


[2019-11-07 09:02:53,007] [           HABApp.AllRules] INFO|          show_rules: ------------------------------------------------------------------------------------------------------------------------
[2019-11-07 09:02:53,008] [           HABApp.AllRules] INFO|          show_rules: FileParameters
[2019-11-07 09:02:53,008] [           HABApp.AllRules] INFO|          show_rules: PatioDoor
[2019-11-07 09:02:53,009] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:02:53,010] [           HABApp.AllRules] INFO|          show_rules: JsonDecode
[2019-11-07 09:02:53,010] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.2
[2019-11-07 09:02:53,010] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.3
[2019-11-07 09:02:53,011] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.4
[2019-11-07 09:02:53,011] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.5
[2019-11-07 09:02:53,011] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.6
[2019-11-07 09:02:53,011] [           HABApp.AllRules] INFO|          show_rules: OutdoorPTZCameras
[2019-11-07 09:02:53,012] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:02:53,012] [           HABApp.AllRules] INFO|          show_rules: OutdoorPTZCameras.2
[2019-11-07 09:02:53,012] [           HABApp.AllRules] INFO|          show_rules: createTimer.2
[2019-11-07 09:02:53,013] [           HABApp.AllRules] INFO|          show_rules: CameraControl
[2019-11-07 09:02:53,013] [           HABApp.AllRules] INFO|          show_rules: CameraControl.2
[2019-11-07 09:02:53,013] [           HABApp.AllRules] INFO|          show_rules: CameraControl.3
[2019-11-07 09:02:53,013] [           HABApp.AllRules] INFO|          show_rules: MCThings111C2
[2019-11-07 09:02:53,014] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:02:53,014] [           HABApp.AllRules] INFO|          show_rules: createTimer.2
[2019-11-07 09:02:53,014] [           HABApp.AllRules] INFO|          show_rules: MCThings111BC
[2019-11-07 09:02:53,014] [           HABApp.AllRules] INFO|          show_rules: getTimestamp
[2019-11-07 09:02:53,015] [           HABApp.AllRules] INFO|          show_rules: createTimer.3
[2019-11-07 09:02:53,015] [           HABApp.AllRules] INFO|          show_rules: createTimer.4
[2019-11-07 09:02:53,015] [           HABApp.AllRules] INFO|          show_rules: MCThings11F73
[2019-11-07 09:02:53,016] [           HABApp.AllRules] INFO|          show_rules: MCThings12733
[2019-11-07 09:02:53,016] [           HABApp.AllRules] INFO|          show_rules: miniRemote
[2019-11-07 09:02:53,016] [           HABApp.AllRules] INFO|          show_rules: miniRemote.2
[2019-11-07 09:02:53,017] [           HABApp.AllRules] INFO|          show_rules: miniRemote.3
[2019-11-07 09:02:53,017] [           HABApp.AllRules] INFO|          show_rules: miniRemote_BackGarden
[2019-11-07 09:02:53,017] [           HABApp.AllRules] INFO|          show_rules: miniRemote_BackGarden.2
[2019-11-07 09:02:53,017] [           HABApp.AllRules] INFO|          show_rules: UpdateHEMTimestamps
[2019-11-07 09:02:53,018] [           HABApp.AllRules] INFO|          show_rules: HEMResetValues
[2019-11-07 09:02:53,018] [           HABApp.AllRules] INFO|          show_rules: HEMCaculateCost
[2019-11-07 09:02:53,018] [           HABApp.AllRules] INFO|          show_rules: HEMPricetNextUpdate
[2019-11-07 09:02:53,018] [           HABApp.AllRules] INFO|          show_rules: Bedtime
[2019-11-07 09:02:53,019] [           HABApp.AllRules] INFO|          show_rules: ModeControl
[2019-11-07 09:02:53,019] [           HABApp.AllRules] INFO|          show_rules: ModeControl.2
[2019-11-07 09:02:53,019] [           HABApp.AllRules] INFO|          show_rules: SetupLightTimers
[2019-11-07 09:02:53,019] [           HABApp.AllRules] INFO|          show_rules: lightTimer
[2019-11-07 09:02:53,020] [           HABApp.AllRules] INFO|          show_rules: lightTimer.2
[2019-11-07 09:02:53,020] [           HABApp.AllRules] INFO|          show_rules: lightTimer.3
[2019-11-07 09:02:53,020] [           HABApp.AllRules] INFO|          show_rules: lightTimer.4
[2019-11-07 09:02:53,021] [           HABApp.AllRules] INFO|          show_rules: lightTimer.5
[2019-11-07 09:02:53,021] [           HABApp.AllRules] INFO|          show_rules: lightTimer.6
[2019-11-07 09:02:53,021] [           HABApp.AllRules] INFO|          show_rules: lightTimer.7
[2019-11-07 09:02:53,021] [           HABApp.AllRules] INFO|          show_rules: lightTimer.8
[2019-11-07 09:02:53,022] [           HABApp.AllRules] INFO|          show_rules: SyncLights
[2019-11-07 09:02:53,022] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:02:53,022] [           HABApp.AllRules] INFO|          show_rules: SyncLights.2
[2019-11-07 09:02:53,022] [           HABApp.AllRules] INFO|          show_rules: createTimer.2
[2019-11-07 09:02:53,023] [           HABApp.AllRules] INFO|          show_rules: SyncLights.3
[2019-11-07 09:02:53,023] [           HABApp.AllRules] INFO|          show_rules: createTimer.3
[2019-11-07 09:02:53,023] [           HABApp.AllRules] INFO|          show_rules: SyncLights.4
[2019-11-07 09:02:53,023] [           HABApp.AllRules] INFO|          show_rules: createTimer.4
[2019-11-07 09:02:53,024] [           HABApp.AllRules] INFO|          show_rules: SyncLights.5
[2019-11-07 09:02:53,024] [           HABApp.AllRules] INFO|          show_rules: createTimer.5
[2019-11-07 09:02:53,024] [           HABApp.AllRules] INFO|          show_rules: SyncLights.6
[2019-11-07 09:02:53,025] [           HABApp.AllRules] INFO|          show_rules: createTimer.6
[2019-11-07 09:02:53,025] [           HABApp.AllRules] INFO|          show_rules: SyncLights.7
[2019-11-07 09:02:53,025] [           HABApp.AllRules] INFO|          show_rules: createTimer.7
[2019-11-07 09:02:53,025] [           HABApp.AllRules] INFO|          show_rules: TestRule
[2019-11-07 09:02:53,026] [           HABApp.AllRules] INFO|          show_rules: LockControl
[2019-11-07 09:02:53,026] [           HABApp.AllRules] INFO|          show_rules: LockIndicator
[2019-11-07 09:02:53,026] [           HABApp.AllRules] INFO|          show_rules: DoorSensors
[2019-11-07 09:02:53,026] [           HABApp.AllRules] INFO|          show_rules: AonSensorUpdates
[2019-11-07 09:02:53,027] [           HABApp.AllRules] INFO|          show_rules: Mousetrap
[2019-11-07 09:02:53,027] [           HABApp.AllRules] INFO|          show_rules: GlassBreakdetector
[2019-11-07 09:02:53,027] [           HABApp.AllRules] INFO|          show_rules: LogAllRules

After editing a rule file a few times (and having it duplicate) - here is the output of the rule (AonSensorUpdates), you can see the duplication's:

[2019-11-07 09:21:52,095] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,097] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,100] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,102] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,102] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,103] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,106] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,107] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,108] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,109] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,111] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,111] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,111] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,113] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000
[2019-11-07 09:21:52,117] [   MyRule.AonSensorUpdates] INFO|    update_timestamp: updated AeonMS6LastUpdatedSA timestamp to : 2019-11-07 09:21:50.991000

the log rule now outputs this:

[2019-11-07 09:21:16,007] [           HABApp.AllRules] INFO|          show_rules: ------------------------------------------------------------------------------------------------------------------------
[2019-11-07 09:21:16,008] [           HABApp.AllRules] INFO|          show_rules: FileParameters
[2019-11-07 09:21:16,008] [           HABApp.AllRules] INFO|          show_rules: PatioDoor
[2019-11-07 09:21:16,009] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:21:16,009] [           HABApp.AllRules] INFO|          show_rules: JsonDecode
[2019-11-07 09:21:16,009] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.2
[2019-11-07 09:21:16,010] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.3
[2019-11-07 09:21:16,010] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.4
[2019-11-07 09:21:16,010] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.5
[2019-11-07 09:21:16,010] [           HABApp.AllRules] INFO|          show_rules: JsonDecode.6
[2019-11-07 09:21:16,011] [           HABApp.AllRules] INFO|          show_rules: OutdoorPTZCameras
[2019-11-07 09:21:16,011] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:21:16,011] [           HABApp.AllRules] INFO|          show_rules: OutdoorPTZCameras.2
[2019-11-07 09:21:16,012] [           HABApp.AllRules] INFO|          show_rules: createTimer.2
[2019-11-07 09:21:16,012] [           HABApp.AllRules] INFO|          show_rules: CameraControl
[2019-11-07 09:21:16,012] [           HABApp.AllRules] INFO|          show_rules: CameraControl.2
[2019-11-07 09:21:16,012] [           HABApp.AllRules] INFO|          show_rules: CameraControl.3
[2019-11-07 09:21:16,013] [           HABApp.AllRules] INFO|          show_rules: MCThings111C2
[2019-11-07 09:21:16,013] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:21:16,013] [           HABApp.AllRules] INFO|          show_rules: createTimer.2
[2019-11-07 09:21:16,013] [           HABApp.AllRules] INFO|          show_rules: MCThings111BC
[2019-11-07 09:21:16,014] [           HABApp.AllRules] INFO|          show_rules: getTimestamp
[2019-11-07 09:21:16,014] [           HABApp.AllRules] INFO|          show_rules: createTimer.3
[2019-11-07 09:21:16,014] [           HABApp.AllRules] INFO|          show_rules: createTimer.4
[2019-11-07 09:21:16,015] [           HABApp.AllRules] INFO|          show_rules: MCThings11F73
[2019-11-07 09:21:16,015] [           HABApp.AllRules] INFO|          show_rules: MCThings12733
[2019-11-07 09:21:16,015] [           HABApp.AllRules] INFO|          show_rules: UpdateHEMTimestamps
[2019-11-07 09:21:16,015] [           HABApp.AllRules] INFO|          show_rules: HEMResetValues
[2019-11-07 09:21:16,016] [           HABApp.AllRules] INFO|          show_rules: HEMCaculateCost
[2019-11-07 09:21:16,016] [           HABApp.AllRules] INFO|          show_rules: HEMPricetNextUpdate
[2019-11-07 09:21:16,016] [           HABApp.AllRules] INFO|          show_rules: Bedtime
[2019-11-07 09:21:16,016] [           HABApp.AllRules] INFO|          show_rules: ModeControl
[2019-11-07 09:21:16,017] [           HABApp.AllRules] INFO|          show_rules: ModeControl.2
[2019-11-07 09:21:16,017] [           HABApp.AllRules] INFO|          show_rules: SetupLightTimers
[2019-11-07 09:21:16,017] [           HABApp.AllRules] INFO|          show_rules: lightTimer
[2019-11-07 09:21:16,017] [           HABApp.AllRules] INFO|          show_rules: lightTimer.2
[2019-11-07 09:21:16,018] [           HABApp.AllRules] INFO|          show_rules: lightTimer.3
[2019-11-07 09:21:16,018] [           HABApp.AllRules] INFO|          show_rules: lightTimer.4
[2019-11-07 09:21:16,018] [           HABApp.AllRules] INFO|          show_rules: lightTimer.5
[2019-11-07 09:21:16,018] [           HABApp.AllRules] INFO|          show_rules: lightTimer.6
[2019-11-07 09:21:16,019] [           HABApp.AllRules] INFO|          show_rules: lightTimer.7
[2019-11-07 09:21:16,019] [           HABApp.AllRules] INFO|          show_rules: lightTimer.8
[2019-11-07 09:21:16,019] [           HABApp.AllRules] INFO|          show_rules: SyncLights
[2019-11-07 09:21:16,020] [           HABApp.AllRules] INFO|          show_rules: createTimer
[2019-11-07 09:21:16,025] [           HABApp.AllRules] INFO|          show_rules: SyncLights.2
[2019-11-07 09:21:16,026] [           HABApp.AllRules] INFO|          show_rules: createTimer.2
[2019-11-07 09:21:16,032] [           HABApp.AllRules] INFO|          show_rules: SyncLights.3
[2019-11-07 09:21:16,032] [           HABApp.AllRules] INFO|          show_rules: createTimer.3
[2019-11-07 09:21:16,033] [           HABApp.AllRules] INFO|          show_rules: SyncLights.4
[2019-11-07 09:21:16,033] [           HABApp.AllRules] INFO|          show_rules: createTimer.4
[2019-11-07 09:21:16,034] [           HABApp.AllRules] INFO|          show_rules: SyncLights.5
[2019-11-07 09:21:16,034] [           HABApp.AllRules] INFO|          show_rules: createTimer.5
[2019-11-07 09:21:16,035] [           HABApp.AllRules] INFO|          show_rules: SyncLights.6
[2019-11-07 09:21:16,036] [           HABApp.AllRules] INFO|          show_rules: createTimer.6
[2019-11-07 09:21:16,036] [           HABApp.AllRules] INFO|          show_rules: SyncLights.7
[2019-11-07 09:21:16,037] [           HABApp.AllRules] INFO|          show_rules: createTimer.7
[2019-11-07 09:21:16,037] [           HABApp.AllRules] INFO|          show_rules: TestRule
[2019-11-07 09:21:16,038] [           HABApp.AllRules] INFO|          show_rules: LockControl
[2019-11-07 09:21:16,039] [           HABApp.AllRules] INFO|          show_rules: LockIndicator
[2019-11-07 09:21:16,041] [           HABApp.AllRules] INFO|          show_rules: DoorSensors
[2019-11-07 09:21:16,042] [           HABApp.AllRules] INFO|          show_rules: miniRemote
[2019-11-07 09:21:16,044] [           HABApp.AllRules] INFO|          show_rules: miniRemote.2
[2019-11-07 09:21:16,046] [           HABApp.AllRules] INFO|          show_rules: miniRemote.3
[2019-11-07 09:21:16,047] [           HABApp.AllRules] INFO|          show_rules: miniRemote_BackGarden
[2019-11-07 09:21:16,049] [           HABApp.AllRules] INFO|          show_rules: miniRemote_BackGarden.2
[2019-11-07 09:21:16,050] [           HABApp.AllRules] INFO|          show_rules: AonSensorUpdates
[2019-11-07 09:21:16,051] [           HABApp.AllRules] INFO|          show_rules: Mousetrap
[2019-11-07 09:21:16,052] [           HABApp.AllRules] INFO|          show_rules: GlassBreakdetector
[2019-11-07 09:21:16,053] [           HABApp.AllRules] INFO|          show_rules: LogAllRules

Which looks no different.

I am thinking that you may be right about caching of rules. I have some rules that I am using as general purpose utilities. For Example createTimer and getTimestamp these are in a file called utils.py which is in the lib directory. This file doesn't get updated (much), but I do assign the utility rules to class members

I would try using the self.get_rule() method, but there seems to be no way of passing parameters using that.

here is the rule that is generating the duplicate output shown above:

class AonSensorUpdates(HABApp.Rule):
    '''
    rule to update timestamps on Aeon sensors
    '''
    def __init__(self):
        super().__init__()
        self.log = logging.getLogger('MyRule.'+self.__class__.__name__)

        # Trigger on item updates
        for i in range(1,5):
            self.listen_event( 'AeonMS6%dSA' % i, self.item_state_update, ItemStateEvent)
            self.listen_event( 'AeonMS6%dF' % i, self.item_state_update, ItemStateEvent)
            self.listen_event( 'AeonMS6%dBE' % i, self.item_state_update, ItemStateEvent)
            self.listen_event( 'AeonMS6%dL' % i, self.item_state_update, ItemStateEvent)
            self.listen_event( 'AeonMS6%dS' % i, self.item_state_update, ItemStateEvent)
            self.listen_event( 'AeonMS6%dW' % i, self.item_state_update, ItemStateEvent)
            self.listen_event( 'AeonMS6%dLA' % i, self.item_state_update, ItemStateEvent)

    def item_state_update(self, event):
        #self.log.debug('Item %s received update %s' % (event.name, event.value))
        device_last_update = 'AeonMS6LastUpdated%s' % event.name.replace('AeonMS6','')[1:]
        getTimestamp(device_last_update, self.log)

AonSensorUpdates()

and here is getTimestamp from the util.py file:

class getTimestamp(HABApp.Rule):
    def __init__(self, date_time_item=None, log=None, format = '%a %H:%M:%S'):
        super().__init__()
        if not log:
            self.log = logging.getLogger('MyRule.'+self.__class__.__name__)
        else:
            self.log = log
        self.format = format
        self.dt = datetime.datetime.now()   #.replace(microsecond=0).isoformat()
        if date_time_item is not None:
            self.Timestamp(date_time_item)   
        #else:
        #    self.log.warning('date_time_item is %s, so not updating timestamp' % date_time_item)
            
    def __repr__(self):
        return self.dt.strftime(self.format)
        
    @property
    def value(self):
        return self.dt
            
    def Timestamp(self, date_time_item):
        if isinstance(date_time_item, str):
            date_time_item = Item.get_item(date_time_item)   
        try:
            time_difference = 1.0
            time_difference = (self.dt - date_time_item.value).total_seconds()
            #self.log.debug('Time difference is %ss' % time_difference)
        except TypeError:
            pass
        except Exception as e:
            self.log.error(e)
            
        if time_difference >= 1.0:
            self.update_timestamp(date_time_item)
        
    def update_timestamp(self, date_time_item):
        self.oh.post_update(date_time_item, self.dt)
        self.log.info('updated %s timestamp to : %s' % (date_time_item.name, date_time_item.value))
        if date_time_item.value is None:
            self.dt = datetime.datetime.now().replace(microsecond=0).isoformat()
            self.oh.post_update(date_time_item, self.dt)
            self.log.info('updated %s timestamp (on retry) to : %s' % (date_time_item.name, date_time_item.value))

I can see that it gets unloaded once, then reloaded 4 times when it is edited and saved.
Any ideas?

@spacemanspiff2007
Copy link
Owner

There are two possibilities to share:
Inherit from your rule in your utility folder (this is the intended use case of the utility folder, to share base classes across rules) or in this case I would make the getTimestamp class a function.
If you want to use get_rule you have to create the rule instance first.

In your case I would create a rule file aeon_sensor with a AeonSensor class and create a rule for each sensor. That way it's wrapped properly and you can then use get_rule to access functions of the rule instance.

@NickWaterton
Copy link
Author

NickWaterton commented Nov 7, 2019

Looking into the mysterious behavior more, I have found that if I touch any file in the rules directory, the file gets unloaded, and loaded again only once.

If, however, I edit the file (with any editor) whether or not I make any changes to the file, the file gets unloaded and then loaded anywhere from 2 to 4 times, ie, one unload, but 2-4 loads.

I have watchdog==0.9.0 on my system (I use if for other things). I'm thinking the problem is with watchdog triggering on multiple events (FileCreatedEvent, DirModifiedEvent, FileModifiedEvent, and so on). Looking at habappfileevent.py you are trapping Created Moved and Modified, all of which trigger RequestFileLoadEvent. Editing a file will trigger several of these events, and cause multiple FileLoad events..

This behavior may be file system dependent, so it might not happen on Windows, say.

I'm running on Ubuntu 18.04 so its an ext4 file system.

@spacemanspiff2007
Copy link
Owner

Yes, I am aware that there might be multiple loads, however I check and unload the file if it is already loaded.

    def request_file_load(...)
        ....
        # Unload if we have already loaded
        if path_str in self.files:
            self.request_file_unload(event)

So even if there are multiple load requests the file should always get unloaded first.

The only other reason I can think if is an uncaught threading issue but then it should be more random (imho).

Could you please post an side-by-side of the corresponding file events from HABApp.Events and the load/unload from HABApp.Rules

@NickWaterton
Copy link
Author

Not sure what you are asking for. I've uploaded logs that show one unload event, followed by 2-4 load events. So it actually happens.

You have no logging in the habappfileevent.py file, so I'm not sure how to post events that aren't logged.

Can you give me specifics?

@NickWaterton
Copy link
Author

NickWaterton commented Nov 8, 2019

So, here is what I have found. (I added some logging)
Editing a file causes up to four RequestFileLoadEvent triggers. These triggers come in very quickly, so the first trigger causes the file to be unloaded via your trap:

# Unload if we have already loaded
        if path_str in self.files:
            self.request_file_unload(event)

However, the other RequestFileLoadEvent triggers arrive while the file is being unloaded and reloaded (depends on how long it takes to unload/load). They do not trigger another request_file_unload because the first thing request_file_unload does is pop the path_str from self.files. The subsequent events now wait at the self.__file_load_lock, where they get executed one after the other as each lock is released., loading the file multiple times

The root cause of this is one of:

  • Multiple triggers for one file event
  • The file load lock block does not check to see if the file is already loaded inside the lock

Here is my log of this happening (I added the file load event request received: log event at the top of the request_file_load routine):

[2019-11-08 10:53:01,453] [              HABApp.Rules]DEBUG|   request_file_load: file load event request received: /etc/openhab2/HABApp/rules/sensors.py
[2019-11-08 10:53:01,453] [              HABApp.Rules]DEBUG| request_file_unload: Removing file: /etc/openhab2/HABApp/rules/sensors.py
[2019-11-08 10:53:01,453] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS61SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,453] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS61F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,454] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS61BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,454] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS61L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,454] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS61S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,454] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS61W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,454] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS61LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,454] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS62SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,455] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS62F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,455] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS62BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,455] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS62L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,456] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS62S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,456] [              HABApp.Rules]DEBUG|   request_file_load: file load event request received: /etc/openhab2/HABApp/rules/sensors.py
[2019-11-08 10:53:01,456] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS62W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,457] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS62LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,457] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS63SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,457] [              HABApp.Rules]DEBUG|   request_file_load: file load event request received: /etc/openhab2/HABApp/rules/sensors.py
[2019-11-08 10:53:01,458] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS63F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,458] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS63BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,458] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS63L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,458] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS63S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,458] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS63W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,459] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS63LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,459] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS64SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,459] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS64F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,459] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS64BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,459] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS64L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,459] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS64S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,459] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS64W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,460] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "AeonMS64LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,460] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "Mousetrap1Tripped" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,460] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "Mousetrap1Battery" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,460] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "Mousetrap1Armed" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,460] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "GlassBreakDetector" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,460] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "GlassBreakDetectorSensorAlarm" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,460] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "GlassBreakDetectorAlarmGeneral" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,461] [                    HABApp]DEBUG|     remove_listener: Removed event listener for "GlassBreakDetectorBattery" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,461] [              HABApp.Rules]DEBUG|              unload: File /etc/openhab2/HABApp/rules/sensors.py successfully unloaded!
[2019-11-08 10:53:01,461] [              HABApp.Rules]DEBUG|   request_file_load: Loading file: /etc/openhab2/HABApp/rules/sensors.py
[2019-11-08 10:53:01,463] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS61SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,463] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS61F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,464] [              HABApp.Rules]DEBUG|   request_file_load: file load event request received: /etc/openhab2/HABApp/rules/sensors.py
[2019-11-08 10:53:01,464] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS61BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,464] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS61L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,465] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS61S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,465] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS61W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,465] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS61LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,465] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS62SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,465] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS62F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,465] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS62BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,465] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS62L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,466] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS62S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,466] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS62W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,466] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS62LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,466] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS63SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,466] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS63F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,466] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS63BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,466] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS63L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,467] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS63S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,467] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS63W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,467] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS63LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,467] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS64SA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,467] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS64F" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,467] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS64BE" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,468] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS64L" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,468] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS64S" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,468] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS64W" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,468] [                    HABApp]DEBUG|        add_listener: Added event listener for "AeonMS64LA" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,468] [                    HABApp]DEBUG|        add_listener: Added event listener for "Mousetrap1Tripped" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,468] [                    HABApp]DEBUG|        add_listener: Added event listener for "Mousetrap1Battery" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,469] [                    HABApp]DEBUG|        add_listener: Added event listener for "Mousetrap1Armed" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,469] [                    HABApp]DEBUG|        add_listener: Added event listener for "GlassBreakDetector" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,469] [                    HABApp]DEBUG|        add_listener: Added event listener for "GlassBreakDetectorSensorAlarm" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,469] [                    HABApp]DEBUG|        add_listener: Added event listener for "GlassBreakDetectorAlarmGeneral" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,469] [                    HABApp]DEBUG|        add_listener: Added event listener for "GlassBreakDetectorBattery" (type <class 'HABApp.openhab.events.item_events.ItemStateEvent'>)
[2019-11-08 10:53:01,469] [              HABApp.Rules] INFO|                load: Added rule "AonSensorUpdates" from sensors.py
[2019-11-08 10:53:01,470] [              HABApp.Rules] INFO|                load: Added rule "Mousetrap" from sensors.py
[2019-11-08 10:53:01,470] [              HABApp.Rules] INFO|                load: Added rule "GlassBreakdetector" from sensors.py
[2019-11-08 10:53:01,470] [              HABApp.Rules]DEBUG|   request_file_load: File /etc/openhab2/HABApp/rules/sensors.py successfully loaded!
[2019-11-08 10:53:01,470] [              HABApp.Rules]DEBUG|   request_file_load: Loading file: /etc/openhab2/HABApp/rules/sensors.py
.
.
.
repeated 3 more times...

This is my crude fix:

    @PrintException
    def request_file_load(self, event: HABApp.core.events.habapp_events.RequestFileLoadEvent):

        path = self.runtime.config.directories.rules / event.filename
        path_str = str(path)

        # Only load existing files
        if not path.is_file():
            log.warning(f'Rule file {path} does not exist and can not be loaded!')
            return None

        # Unload if we have already loaded
        if path_str in self.files:
            self.request_file_unload(event)

        try:
            # serialize loading
            with self.__file_load_lock:
                # Ignore if we have already loaded
                if path_str in self.files:
                    return None
                log.debug(f'Loading file: {path}')
                with self.__rulefiles_lock:
                    self.files[path_str] = file = RuleFile(self, path)
                file.load()
        except Exception:
            log.error(f"Could not (fully) load {path}!")
            for l in traceback.format_exc().splitlines():
                log.error(l)
            return None

        log.debug(f'File {path_str} successfully loaded!')

        # Do simple checks which prevent errors
        file.check_all_rules()

Tested and working. No more duplication in logs/multiple rules running etc.
I'm sure you can come up with a better solution, but works for now.

@spacemanspiff2007
Copy link
Owner

I suspected this might be the problem, guess I'll make loading and unloading completely blocking.
Thanks for you help.

@NickWaterton
Copy link
Author

I downloaded your latest version. The fix for this issue is quite complicated. It also causes a file to be unloaded and reloaded four times one after the other for each change in the file.

Would it not be better to simply use a self.__load_lock.locked() test and return if the file is busy being unloaded/loaded instead of all the locking and unlocking/finally steps? then you could use the with lock construct, and the file would only get unloaded/loaded once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants