Beispiel #1
0
 def test_3_sec_callback(self):
     callback = Mock()
     callback.test.return_value = True
     rt = CTimer(3)
     rt.action(callback.test, ())
     rt.start()
     self.assertFalse(callback.test.called)
     time.sleep(4)
     self.assertTrue(callback.test.called)
Beispiel #2
0
 def test_3_sec_callback(self):
     callback = Mock()
     callback.test.return_value = True
     rt = CTimer(3)
     rt.action(callback.test, ())
     rt.start()
     self.assertFalse(callback.test.called)
     time.sleep(4)
     self.assertTrue(callback.test.called)
Beispiel #3
0
    def idle(self, *args, **kwargs):
        command = kwargs.get('command', None)
        source = kwargs.get('source', None)
        mapped = kwargs.get(Attribute.MAPPED, None)
        secs = kwargs.get('secs', None)
        if secs:
            timer = CTimer()
            timer.interval = secs
            timer.action(self.command, (mapped, ), source=self, original=source, source_property=Property.IDLE)
#            self._idle_timer = timer
            self._idle_timer.update({(command, source): {Attribute.SECS: secs,
                                                         Attribute.MAPPED: mapped,
                                                         'timer': timer}})
Beispiel #4
0
    def _trigger_start(self, command, source, *args, **kwargs):
        original_command = kwargs.get('original', None)
        trigger = self._triggers.get((command, source), None)
        if not trigger and original_command:
            trigger = self._triggers.get((original_command, source), None)
        if not trigger:
            trigger = self._triggers.get((command, None), None)
        if not trigger:
            trigger = self._triggers.get((original_command, None), None)
##       trigger = self._match_condition(command, source, self._triggers)

        if trigger and self._match_condition_item(trigger):
            timer = self._trigger_timers.get(trigger[Attribute.MAPPED], None)
            if not timer:
                timer = CTimer()
            timer.stop()
            if trigger[Attribute.SECS] > 0:
                timer.action(self.command, (trigger[Attribute.MAPPED], ), source=self, original=source)
                timer.interval = trigger[Attribute.SECS]
                self._trigger_timers.update({trigger[Attribute.MAPPED]: timer} )
                timer.start()
                self._logger.debug('{name} command "{command}" from source "{source}" trigger started, mapped to "{mapped}" waiting {secs} secs. '.format(
                                                                                      name=self.name,
                                                                                      source=source.name if source else None,
                                                                                      command=command,
                                                                                      mapped=trigger[Attribute.MAPPED],
                                                                                      secs=trigger[Attribute.SECS],
                                                                                ))
Beispiel #5
0
 def test_no_sec_callback(self):
     callback = Mock()
     callback.test.return_value = True
     rt = CTimer()
     rt.interval = 60
     rt.action(callback.test, ())
     rt.start()
     time.sleep(3)
     rt.stop()
     self.assertFalse(callback.test.called)
Beispiel #6
0
 def test_no_sec_callback(self):
     callback = Mock()
     callback.test.return_value = True
     rt = CTimer()
     rt.interval = 60
     rt.action(callback.test, ())
     rt.start()
     time.sleep(3)
     rt.stop()
     self.assertFalse(callback.test.called)
Beispiel #7
0
 def mapped(self, *args, **kwargs):
     command = kwargs.get('command', None)
     mapped = kwargs.get('mapped', None)
     source = kwargs.get('source', None)
     secs = kwargs.get('secs', None)
     timer = None
     commands = command
     if not isinstance(command, tuple):
         commands = (command, )
     for c in commands:
         if secs:
             timer = CTimer()
             timer.interval = secs
             timer.action(self.command, (mapped, ), source=self, original=source)
 #        self._maps.append({'command': command, 'mapped': mapped, 'source': source})
         sources = source
         if not isinstance(source, tuple):
             sources = (source ,)
         for s in sources:
             self._maps.update({(c, s): (mapped, timer)})
Beispiel #8
0
    def delay(self, *args, **kwargs):
        commands = kwargs.get('command', None)
        if (not isinstance(commands, tuple)):
            commands = (commands, )
        mapped = kwargs.get('mapped', None)
        sources = kwargs.get('source', None)
        if (not isinstance(sources, tuple)):
            sources = (sources, )
        secs = kwargs.get('secs', None)

        for command in commands:
            for source in sources:
                if not mapped:
                    m = command
                else:
                    m = mapped
                timer = CTimer()
                timer.interval=secs
                timer.action(self.command, (m, ), source=self, original=source, source_property=Property.DELAY)
                self._delays.update({(command, source): {'mapped': m, 'secs': secs, 'timer': timer}})
        return True
Beispiel #9
0
 def test_isAlive(self):
     rt = CTimer()
     rt.interval = 2
     rt.start()
     self.assertTrue(rt.isAlive())
     time.sleep(3)
     self.assertFalse(rt.isAlive())        
Beispiel #10
0
 def test_isAlive(self):
     rt = CTimer()
     rt.interval = 2
     rt.start()
     self.assertTrue(rt.isAlive())
     time.sleep(3)
     self.assertFalse(rt.isAlive())
Beispiel #11
0
 def test_double_timer_bug(self):
     callback = Mock()
     callback.test.return_value = True
     rt = CTimer(3)
     rt.action(callback.test, ())
     rt.start()
     rt.start()
     rt.stop()
     self.assertFalse(callback.test.called)
     time.sleep(4)
     self.assertFalse(callback.test.called)
Beispiel #12
0
 def test_double_timer_bug(self):
     callback = Mock()
     callback.test.return_value = True
     rt = CTimer(3)
     rt.action(callback.test, ())
     rt.start()
     rt.start()
     rt.stop()
     self.assertFalse(callback.test.called)
     time.sleep(4)
     self.assertFalse(callback.test.called)
Beispiel #13
0
 def _delay_start(self, command, source, *args, **kwargs):
     original_command = kwargs.get('original', None)
     delay = self._get_delay(command, source, original_command, include_zero=True)
     if delay:
         timer = self._delay_timers.get(delay['mapped'], None)
         if not timer:
             timer = CTimer()
         timer.stop()
         if delay['secs'] > 0:
             timer.action(self.command, (delay['mapped'], ), source=self, original=source, source_property=Property.DELAY)
             timer.interval = delay['secs']
             self._delay_timers.update({delay['mapped']: timer} )
             timer.start()
             self._logger.debug('{name} command "{command}" from source "{source}" delayed, mapped to "{mapped}" waiting {secs} secs. '.format(
                                                                                   name=self.name,
                                                                                   source=source.name if source else None,
                                                                                   command=command,
                                                                                   mapped=delay['mapped'],
                                                                                   secs=delay['secs'],
                                                                             ))
Beispiel #14
0
class InterfaceDevice(StateDevice):
    def __init__(self, address=None, *args, **kwargs):
        self._address = address
        super(InterfaceDevice, self).__init__(*args, **kwargs)

    def _initial_vars(self, *args, **kwargs):
        super(InterfaceDevice, self)._initial_vars(*args, **kwargs)
        self._interfaces = []
        self._sync = False
        self._sync_timer = None
        self._read_only = False
        self._send_always = config.device_send_always
        self._previous_interface_command = None

    @property
    def address(self):
        return self._address

    @address.setter
    def address(self, value):
        self._address = value
        return self._address

    def addressMatches(self, address):
        match = self.address == None or self.address == address
        if not match:
            try:
                match = self.address.lower() == address.lower()
            except Exception as ex:
                pass
        return match

    def _add_device(self, device):
        try:
            device.onCommand(
                device=self)  # Register with the interface to receive events
            self._interfaces.append(device)
            self.delay(command=Command.ON, source=device, secs=0)
            self.delay(command=Command.OFF, source=device, secs=0)
            self._logger.debug("{name} added new interface {interface}".format(
                name=self.name,
                interface=device.name,
            ))
            return True
        except Exception as ex:
            return super(InterfaceDevice, self)._add_device(device)

    def _delegate_command(self, command, *args, **kwargs):
        original_state = kwargs.get('original_state', None)
        source = kwargs.get('source', None)
        original = kwargs.get('original', None)
        if not self._read_only:
            for interface in self._interfaces:
                if source != interface and original != interface:
                    new_state = self._command_to_state(command, None)
                    if self._send_always or (not self._send_always
                                             and original_state != new_state):
                        self._previous_interface_command = command
                        try:
                            self._logger.debug(
                                "{name} Send command '{command}' to interface '{interface}'"
                                .format(name=self.name,
                                        command=command,
                                        interface=interface.name))
                            self._send_command_to_interface(
                                interface, self._address, command)
#                             if isinstance(command, tuple):
# #                                getattr(interface, command[0])(self._address, *command[1:])
#                                 self._send_command_to_interface(interface, self._address, *command[1:])
#                             else:
# #                                getattr(interface, command)(self._address)
#                                 self._send_command_to_interface(interface, self._address, command)
                        except Exception as ex:
                            self._logger.error(
                                "{name} Could not send command '{command}' to interface '{interface}.  Exception: {exc}'"
                                .format(
                                    name=self.name,
                                    command=command,
                                    interface=interface.name,
                                    exc=str(ex),
                                ))
                    else:
                        self._logger.debug(
                            "{name} is already at this new state {state} originally {original_state} for command {command} -> {new_state}, do not send to interface"
                            .format(
                                name=self.name,
                                state=self.state,
                                original_state=original_state,
                                command=command,
                                new_state=new_state,
                            ))
                else:
                    self._logger.debug(
                        "{name} do not send to interface because either the current source {source} or original source {original} is the interface itself."
                        .format(
                            name=self.name,
                            state=self.state,
                            source=source,
                            original=original,
                            command=command,
                        ))
        return super(InterfaceDevice,
                     self)._delegate_command(command, *args, **kwargs)

    def _send_command_to_interface(self, interface, address, command):
        if isinstance(command, tuple):
            getattr(interface, command[0])(self._address, *command[1:])
        else:
            getattr(interface, command)(self._address)

    def sync(self, value):
        self._sync = value
        if value:
            self._start_sync()
        else:
            self._stop_sync()
        return self._sync

    def _start_sync(self):
        # get a random number of secs from 30 minutes to an hour
        offset = random.randint(0, 30 * 60) + (30 * 60)
        self._sync_timer = CTimer(offset)
        self._sync_timer.action(self._run_sync, ())
        self._sync_timer.start()

    def _stop_sync(self):
        self._sync_timer.stop()

    def _run_sync(self):
        if self.interface:
            getattr(self.interface, self._state)()
        self._start_sync()

    def read_only(self, value=None):
        if value:
            self._read_only = value
        return self._read_only

    def send_always(self, value=False):
        if value:
            self._send_always = value
        return self._send_always
Beispiel #15
0
 def _start_sync(self):
     # get a random number of secs from 30 minutes to an hour
     offset = random.randint(0, 30 * 60) + (30 * 60)
     self._sync_timer = CTimer(offset)
     self._sync_timer.action(self._run_sync, ())
     self._sync_timer.start()
Beispiel #16
0
class StateDevice(PytomationObject):
    STATES = [State.UNKNOWN, State.ON, State.OFF, State.LEVEL]
    COMMANDS = [Command.ON, Command.OFF, Command.LEVEL, Command.PREVIOUS,
                Command.TOGGLE, Command.AUTOMATIC, Command.MANUAL, Command.INITIAL, Command.STATUS]
    DEFAULT_COMMAND = Command.TOGGLE
    DEFAULT_NUMERIC_COMMAND = Command.LEVEL
    _delegates_state_change = []

    def __init__(self, *args, **kwargs):
        self._command_lock = _thread.allocate_lock()
        super(StateDevice, self).__init__(*args, **kwargs)
        if not kwargs.get('devices', None) and len(args)>0:
            kwargs.update({'devices': args[0]})
        self._initial_vars(*args, **kwargs)
        self._process_kwargs(kwargs)
        self._initial_from_devices(*args, **kwargs)
        if not self.state or self.state == State.UNKNOWN:
            self.command(Command.INITIAL, source=self)

    def _initial_vars(self, *args, **kwargs):
        self._state = State.UNKNOWN
        self._previous_state = self._state
        self._previous_command = None
        self._last_set = datetime.now()
        self._changes_only = False
        self._delegates = []
        self._delegates_state_change = []
        self._times = []
        self._maps = {}
        self._delays = {}
        self._delay_timers = {}
        self._triggers = {}
        self._trigger_timers = {}
        self._ignores = {}
        self._restrictions = {}
        self._idle_timer = {}
        self._idle_command = None
        self._devices = []
        self._automatic = True
        self._retrigger_delay = None
#        self.invert(False)


    def invert(self, *args, **karwgs):
        if not self._maps.get((Command.ON, None), None):
            if args[0]:
                self.mapped(command=Command.ON, mapped=Command.OFF)
            else:
                self.mapped(command=Command.ON, mapped=Command.ON)
        if not self._maps.get((Command.OFF, None), None):
            if args[0]:
                self.mapped(command=Command.OFF, mapped=Command.ON)
            else:
                self.mapped(command=Command.OFF, mapped=Command.OFF)

    @property
    def state(self):
        return self._get_state()

    @state.setter
    def state(self, value, *args, **kwargs):
        return self._set_state(value, *args, **kwargs)

    def _get_state(self):
        return self._state

    def set_state(self, value, *args, **kwargs):
        return self._set_state(value, *args, **kwargs)

    def _set_state(self, value, *args, **kwargs):
        source = kwargs.get('source', None)
        self._previous_state = self._state
        self._last_set = datetime.now()
        if value != self._state:
            self._state = value
            self._delegate_state_change(value, prev=self._state, source=source)
        return self._state

    def __getattr__(self, name):
        # Give this object methods for each command supported
        if self._is_valid_command(name):
            return lambda *a, **k: self.command(name, *a, sub_state=a, **k)

    def command(self, command, *args, **kwargs):
        # Lets process one command at a time please
        with self._command_lock:
            source = kwargs.get('source', None)
            source_property = kwargs.get('source_property', None)
#             if source_property == Property.DELAY:
#                 pass
#             if source_property == Property.IDLE:
#                 pass
#             if source_property == None:
#                 pass
            if not self._is_ignored(command, source):
                m_command = self._process_maps(*args, command=command, **kwargs)
                if m_command != command:
                    self._logger.debug("{name} Map from '{command}' to '{m_command}'".format(
                                                                            name=self.name,
                                                                            command=command,
                                                                            m_command=m_command,
                                                                                             ))

                (state, map_command) = self._command_state_map(m_command, *args, **kwargs)

                if map_command == Command.MANUAL:
                    self._automatic = False
                elif map_command == Command.AUTOMATIC:
                    self._automatic = True

                if self._is_restricted(map_command, source):
                    state = None
                    map_command = None

                if state and map_command and self._is_valid_state(state):
                    if not self._filter_retrigger_delay(command=map_command, source=source, new_state=state, original_state=self.state, original=command):

                        if source == self or (not self._get_delay(map_command, source, original=command) or not self._automatic):
                            original_state = self.state
                            self._logger.info('{name} changed state from "{original_state}" to "{state}", by command {command} from {source}'.format(
                                                              name=self.name,
                                                              state=state,
                                                              original_state=original_state,
                                                              command=map_command,
                                                              source=source.name if source else None,
                                                                                                                          ))
                            self._set_state(state, source=source)
                            self._cancel_delays(map_command, source, original=command, source_property=source_property)
                            if self._automatic:
                                self._idle_start(command=map_command, source=source, original_command=command)
                            self._previous_command = map_command
                            self._delegate_command(map_command, original_state=original_state, *args, **kwargs)
                            if self._automatic:
                                self._trigger_start(map_command, source, original=command)
                            self._logger.debug('{name} Garbarge Collection queue:{queue}'.format(
                                                                                        name=self.name,
                                                                                        queue=str(StateDevice.dump_garbage()),
                                                                                                 ))
                        else:
                            self._logger.debug("{name} command {command} from {source} was delayed".format(
                                                                                                       name=self.name,
                                                                                                       command=command,
                                                                                                       source=source.name if source else None
                                                                                                       ))
                            self._delay_start(map_command, source, original=command)
                    else:
                        # retrigger
                        self._logger.debug("{name} Retrigger delay ignored command {command} from {source}".format(
                                                                                               name=self.name,
                                                                                               command=command,
                                                                                               source=source.name if source else None
                                                                                               ))
                elif command == Command.STATUS:
                    # If this is a status request, dont set state just pass along the command.
                    self._logger.debug("{name} delgating 'Status' command from {source}".format(
                                                                                               name=self.name,
                                                                                               command=command,
                                                                                               source=source.name if source else None
                                                                                               ))
                    self._delegate_command(command, original_state=self.state, *args, **kwargs)
                else:
                    self._logger.debug("{name} mapped to nothing, ignored command {command} from {source}".format(
                                                                                               name=self.name,
                                                                                               command=command,
                                                                                               source=source.name if source else None
                                                                                               ))
            else:
                self._logger.debug("{name} ignored command {command} from {source}".format(
                                                                                           name=self.name,
                                                                                           command=command,
                                                                                           source=source.name if source else None
                                                                                           ))

    def _command_state_map(self, command, *args, **kwargs):
        state = None
        state = self._command_to_state(command, state)
        m_command = self._state_to_command(state, command)
        if command == Command.LEVEL or (isinstance(command, tuple) and command[0] == Command.LEVEL):
            if isinstance(command, tuple):
                state = (State.LEVEL, command[1:])
                if len(command[1:]) > 1:
                    state = sum([(State.LEVEL, ), command[1:]], ())
                else:
                    state = (State.LEVEL, command[1])
#                m_command = command
            else:
                state = (State.LEVEL, kwargs.get('sub_state', (0,))[0])
#                m_command = (Command.LEVEL,  kwargs.get('sub_state', (0,) ))
            m_command = self._state_to_command(state, m_command)
        elif command == Command.SETPOINT or (isinstance(command, tuple) and command[0] == Command.SETPOINT):
            if isinstance(command, tuple):
                state = (State.SETPOINT, command[1:])
                if len(command[1:]) > 1:
                    state = sum([(State.SETPOINT, ), command[1:]], ())
                else:
                    state = (State.SETPOINT, command[1])
#                m_command = command
            else:
                state = (State.SETPOINT, kwargs.get('sub_state', (0,))[0])
        elif isinstance(command, tuple) and self._is_valid_command(command[0]) and command[0] != Command.LEVEL:
            m_command = command
            state = self._previous_state
        elif command == Command.PREVIOUS:
            state = self._previous_state
            m_command = self._state_to_command(state, m_command)
        elif command == Command.TOGGLE:
            state = self.toggle_state()
            m_command = self._state_to_command(state, m_command)
        elif command == Command.INITIAL:
            state = self.state
        elif command == Command.AUTOMATIC or command == Command.MANUAL:
            m_command = command
        return (state, m_command)

    def toggle_state(self):
        if self.state == State.ON:
            state = State.OFF
        elif self.state == State.OFF:
            state = State.ON
        elif self.state == State.LOCKED:
            state = State.UNLOCKED
        elif self.state == State.UNLOCKED:
            state = State.LOCKED
        else:
            state = State.OFF #default to toggle off, to account for dimmed lights
        return state

    def _command_to_state(self, command, state):
        # Try to map the same state ID
        try:
#            state = getattr(State, command)
            if isinstance(command, tuple):
                primary = CommandStateMap.command_to_state.get(command[0], None)
                if primary:
                    tlist = list(command)
                    tlist[0] = primary
                    return tuple(tlist)
                else:
                    primary = command[0]
            else:
                command = CommandStateMap.command_to_state.get(command, command)
                primary = command
            for attribute in dir(State):
                if getattr(State, attribute) == primary:
                    return command
        except Exception as ex:
            self._logger.debug("{name} Could not find command to state for {command}".format(
                                                                            name=self.name,
                                                                            command=command,
                                                                            ))
        return state

    def _state_to_command(self, state, command):
        try:
#            return Command['state']
            if isinstance(state, tuple):
                primary = CommandStateMap.state_to_command.get(state[0], None)
                if primary:
                    tlist = list(state)
                    tlist[0] = primary
                    return tuple(tlist)
                else:
                    primary = state[0]
            else:
                state = CommandStateMap.state_to_command.get(state, state)
                primary = state
            for attribute in dir(Command):
                if getattr(Command, attribute) == primary:
                    return state
            return command
        except Exception as ex:
            self._logger.debug("{name} could not map state {state} to command".format(
                                                                        name=self.name,
                                                                        state=state,
                                                                                                    ))
            return command

    def _process_kwargs(self, kwargs):
        # Process each initializing attribute as a method call on this object
        # devices have priority
        if kwargs.get('devices', None):
            try:
                getattr(self, 'devices')( **kwargs['devices'])
            except Exception as ex:
                getattr(self, 'devices')( kwargs['devices'])

        if kwargs.get('commands', None):
            self.COMMANDS = kwargs.get('commands')

        if kwargs.get('states', None):
            self.COMMANDS = kwargs.get('states')

        # run through the rest
        for k, v in kwargs.items():
            if k.lower() not in ('devices','commands','states'):
                attribute = getattr(self, k)
                if not attribute:
                    self._logger.error('Keyword: "{0}" not found in object construction.'.format(k))
                else:
                    try:
                        attribute(**v)
                    except ValueError as ex:
                        raise ex
                    except Exception as ex:
                        if callable(attribute):
                            if isinstance(v, tuple):
                                for v1 in v:
                                    try:
                                        attribute(**v1)
                                    except Exception as ex:
                                        attribute(v1)
                            else:
                                attribute(v)
                        else:
                            attribute = v


    def _process_maps(self, *args, **kwargs):
        source = kwargs.get(Attribute.SOURCE, None)
        command = kwargs.get(Attribute.COMMAND, None)

        self._logger.debug("{name} MAPS dump: {maps}".format(
                                                name=self.name,
                                                maps=str(self._maps),
                                                            ))

        for (c, s), (target, timer) in self._maps.items():
            commands = []
            sources = []
            if isinstance(s, tuple):
                sources = s
            else:
                sources = (s, )
            if isinstance(c, tuple):
                commands = c
            else:
                commands = (c, )

            # Find specific first
            if command in commands and source in sources:
                if not timer or not self._automatic:
                    return target
                else:
                    self._logger.debug('{name} Map Timer Started for command "{command}" from source "{source}" will send "{target}" in "{secs}" secs.'.format(
                                            name=self.name,
                                            source=source.name if source else None,
                                            command=command,
                                            target=target,
                                            secs=timer.interval,
                    ))
                    timer.action(self.command, (target, ), source=self, original=source)
                    timer.restart()
                    return None

            # Go for a more general match next
            if command in commands and None in sources:
                if not timer or not self._automatic:
                    return target
                else:
                    self._logger.debug('{name} Map Timer Started for command "{command}" from source "{source}" will send "{target}" in "{secs}" secs.'.format(
                                            name=self.name,
                                            source=source,
                                            command=command,
                                            target=target,
                                            secs=timer.interval,
                    ))
                    timer.action(self.command, (target, ), source=self, original=source)
                    timer.restart()
                    return None


        return command

    def _is_valid_state(self, state):
        isFound = state in self.STATES
        if not isFound:
            try:
                isFound = state[0] in self.STATES
            except:
                pass
        if not isFound:
            self._logger.debug("{name} tried to be set to invalid state {state}".format(
                                                                        name=self.name,
                                                                        state=state,
                                                                                        ))
        return isFound

    def _is_valid_command(self, command):
        return command in self.COMMANDS

    def initial(self, state):
        try: # Check to see if this is a device reference
            last_command = state.last_command
            self.command(last_command, source=state)
#            (m_state, m_command) = self._command_state_map(last_command)
#            self.state = m_state
        except: # Just a value
#            self.state = state
            self.command(self._state_to_command(state, None), source=None)

    def time(self, *args, **kwargs):
        # time, command
        times = kwargs.get('time', None)
        command = kwargs.get('command', State.UNKNOWN)

        if times:
            if not isinstance( times, tuple) or (isinstance(times, tuple) and isinstance(times[0], int)):
                times = (times, )
            for time in times:
                timer = CronTimer()
                if isinstance(time, tuple):
                    timer.interval(*time)
                else:
                    timer.interval(*CronTimer.to_cron(time))
                timer.action(self.command, (command))
                timer.start()
                self._times.append((command, timer))

    def on_command(self, device=None, remove=False):
        if not remove:
            self._delegates.append(device)
        else:
            self._delegates.remove(device)

    def _delegate_command(self, command, *args, **kwargs):
        source = kwargs.get('source', None)
        original_state = kwargs.get('original_state', None)

        for delegate in self._delegates:
#            print "here {name} s:{source} d:{delegate}".format(
#                                                               name=self.name,
#                                                               source=source.name if source else None,
#                                                               delegate=delegate.name if delegate else None,
#                                                               )
            if delegate != self and source != delegate and \
                (not self._changes_only or \
                (self._changes_only and self._state != original_state)):
                self._logger.debug("{name} delegating command {command} from {source} to object {delegate}".format(
                                                                                   name=self.name,
                                                                                   command=command,
                                                                                   source=source.name if source else None,
                                                                                   delegate=delegate.name,
                                                                           ))
                delegate.command(command=command, source=self)
            else:
                self._logger.debug("{name} Avoid duplicate delegation of {command} from {source} to object {delegate}".format(
                                                                                   name=self.name,
                                                                                   command=command,
                                                                                   source=source.name if source else None,
                                                                                   delegate=delegate.name,
                                                                           ))


    def device_list(self):
        if len(self._devices) == 0:
            return None
        device_ids = []
        for device in self._devices:
            device_ids.append(device.type_id)
        return device_ids


    def _delegate_state_change(self, state, *args, **kwargs):
        for _delegate in self._delegates_state_change:
            try:
                _delegate(state,
                          prev=kwargs.get('prev', None),
                          source=kwargs.get('source',None),
                          device=self)
            except Exception as ex:
                pass
        for _delegate in StateDevice._delegates_state_change:
            try:
                _delegate(state,
                          prev=kwargs.get('prev', None),
                          source=kwargs.get('source',None),
                          device=self)
            except Exception as ex:
                pass


    def devices(self, *args, **kwargs):
        devices = args[0]

        if not isinstance(devices, tuple):
            devices = (devices, )

        for device in devices:
            if device:
                self._add_device(device)

    def add_device(self, device):
        return self._add_device(device)

    def _add_device(self, device):
        if not isinstance(device, dict):
            self._devices.append(device)
            self._logger.debug("{name} added new device {device}".format(
                                                                         name=self.name,
                                                                         device=device.name,
                                                                         ))
            return device.on_command(device=self)
        return True

    def remove_device(self, device):
        if device in self._devices:
            device.on_command(device=self, remove=True)
            self._devices.remove(device)
            return True
        else:
            return False

    def mapped(self, *args, **kwargs):
        command = kwargs.get('command', None)
        mapped = kwargs.get('mapped', None)
        source = kwargs.get('source', None)
        secs = kwargs.get('secs', None)
        timer = None
        commands = command
        if not isinstance(command, tuple):
            commands = (command, )
        for c in commands:
            if secs:
                timer = CTimer()
                timer.interval = secs
                timer.action(self.command, (mapped, ), source=self, original=source)
    #        self._maps.append({'command': command, 'mapped': mapped, 'source': source})
            sources = source
            if not isinstance(source, tuple):
                sources = (source ,)
            for s in sources:
                self._maps.update({(c, s): (mapped, timer)})

    def delay(self, *args, **kwargs):
        commands = kwargs.get('command', None)
        if (not isinstance(commands, tuple)):
            commands = (commands, )
        mapped = kwargs.get('mapped', None)
        sources = kwargs.get('source', None)
        if (not isinstance(sources, tuple)):
            sources = (sources, )
        secs = kwargs.get('secs', None)

        for command in commands:
            for source in sources:
                if not mapped:
                    m = command
                else:
                    m = mapped
                timer = CTimer()
                timer.interval=secs
                timer.action(self.command, (m, ), source=self, original=source, source_property=Property.DELAY)
                self._delays.update({(command, source): {'mapped': m, 'secs': secs, 'timer': timer}})
        return True

    def _get_delay(self, command, source, original=None, include_zero=False):
        delay = self._delays.get((command, source), None)
        if not delay and original:
            delay = self._delays.get((original, source), None)
        if delay:
            if delay['secs'] > 0 or include_zero:
                return delay
            else:
                return None

        delay = self._delays.get((command, None), None)
        if not delay and original:
            delay = self._delays.get((original, None), None)
        if delay and (delay['secs'] > 0 or include_zero):
            return delay

        return None

    def _cancel_delays(self, command, source, original=None, source_property=None):
        if not self._get_delay(command, source, original) and source_property != Property.IDLE:
            for c, timer in self._delay_timers.items():
                self._logger.debug("{name} stopping an existing delay timer of '{interval}' secs for command: '{command}' because the same non-delayed command was now processed. From {source} original command {original}".format(
                                                                                           name=self.name,
                                                                                           command=command,
                                                                                           source=source.name if source else None,
                                                                                           interval=timer.interval,
                                                                                           original=original,
                                                                                           ))
                timer.stop()

    def _delay_start(self, command, source, *args, **kwargs):
        original_command = kwargs.get('original', None)
        delay = self._get_delay(command, source, original_command, include_zero=True)
        if delay:
            timer = self._delay_timers.get(delay['mapped'], None)
            if not timer:
                timer = CTimer()
            timer.stop()
            if delay['secs'] > 0:
                timer.action(self.command, (delay['mapped'], ), source=self, original=source, source_property=Property.DELAY)
                timer.interval = delay['secs']
                self._delay_timers.update({delay['mapped']: timer} )
                timer.start()
                self._logger.debug('{name} command "{command}" from source "{source}" delayed, mapped to "{mapped}" waiting {secs} secs. '.format(
                                                                                      name=self.name,
                                                                                      source=source.name if source else None,
                                                                                      command=command,
                                                                                      mapped=delay['mapped'],
                                                                                      secs=delay['secs'],
                                                                                ))

    @property
    def idle_time(self):
        difference = datetime.now() - self._last_set
        return difference.total_seconds()

    def idle(self, *args, **kwargs):
        command = kwargs.get('command', None)
        source = kwargs.get('source', None)
        mapped = kwargs.get(Attribute.MAPPED, None)
        secs = kwargs.get('secs', None)
        if secs:
            timer = CTimer()
            timer.interval = secs
            timer.action(self.command, (mapped, ), source=self, original=source, source_property=Property.IDLE)
#            self._idle_timer = timer
            self._idle_timer.update({(command, source): {Attribute.SECS: secs,
                                                         Attribute.MAPPED: mapped,
                                                         'timer': timer}})

    def _idle_start(self, *args, **kwargs):
        command = kwargs.get('command', None)
        source = kwargs.get('source', None)
        original_command = kwargs.get('original_command', None)
        idle = self._idle_timer.get((command, source), None)
        if not idle:
            idle = self._idle_timer.get((original_command, source), None)
        if not idle:
            idle = self._idle_timer.get((None, source), None)
        if not idle:
            idle = self._idle_timer.get((Command, None), None)
        if not idle:
            idle = self._idle_timer.get((None, None), None)
        if idle:
            if idle[Attribute.MAPPED] and source != self and self.state != State.OFF:
                timer = idle['timer']
                timer.action(self.command, (idle[Attribute.MAPPED], ), source=self, original=source, source_property=Property.IDLE)
                timer.start()


    def ignore(self, *args, **kwargs):
        commands = kwargs.get('command', None)
        sources = kwargs.get('source', None)
        start = kwargs.get(Attribute.START, None)
        end = kwargs.get(Attribute.END, None)

        if not isinstance(commands, tuple):
            commands = (commands, )
        if not isinstance(sources, tuple):
            sources = (sources, )

        for command in commands:
            for source in sources:
                self._ignores.update({
                                      (command, source): {
                                                          Attribute.START: CronTimer.to_cron(start),
                                                         Attribute.END: CronTimer.to_cron(end),
                                                     }
                                      })
                self._logger.debug("{name} add ignore for {command} from {source}".format(
                                                                                        name=self.name,
                                                                                        command=command,
                                                                                        source=source.name if source else None,
                                                                                        ));

    def _is_ignored(self, command, source):
        self._logger.debug("{name} check ignore for {command} from {source}".format(
                                        name=self.name,
                                        command=command,
                                        source=source.name if source else None,
                                        ));


        match = self._match_condition(command, source, self._ignores)
        if match:
            return True
        else:
            return False


    def restriction(self, *args, **kwargs):
        states = kwargs.get(Attribute.STATE, None)
        sources = kwargs.get(Attribute.SOURCE, None)
        targets = kwargs.get(Attribute.TARGET, None)
        start = kwargs.get(Attribute.START, None)
        end = kwargs.get(Attribute.END, None)

        if not isinstance(states, tuple):
            states = (states, )
        if not isinstance(sources, tuple):
            sources = (sources, )
        if not isinstance(targets, tuple):
            targets = (targets, )

        for state in states:
            for source in sources:
                for target in targets:
                    self._restrictions.update({
                                          (state, source, target): {
                                                              Attribute.START: CronTimer.to_cron(start),
                                                             Attribute.END: CronTimer.to_cron(end),
                                                         }
                                          })
                    self._logger.debug("{name} add restriction for {state} from {source} on {target}".format(
                                                    name=self.name,
                                                    state=state,
                                                    target=target,
                                                    source=source.name if source else None,
                                                    ));

    def _is_restricted(self, command, source):
        if self._restrictions and source != self:
            for state, source, target in self._restrictions:
                c_state = source.state
                if (state == c_state and (target==None or target==command)):
                    if (self._match_condition_item(self._restrictions.get((state, source, target)))):
                        self._logger.debug("{name} Restricted. ignoring".format(
                                                                             name=self.name,
                                                                             ))
                        return True

        return False

    def _match_condition(self, command, source, conditions):
        # Specific match first
        cond = self._match_condition_item(self._get_condition(command, source, conditions))
        if cond:
            return cond
        cond = self._match_condition_item(self._get_condition(command, None, conditions))
        if cond:
            return cond
        cond = self._match_condition_item(self._get_condition(None, source, conditions))
        if cond:
            return cond
        cond = self._match_condition_item(self._get_condition(None, None, conditions))
        if cond:
            return cond

    def _get_condition(self, command, source, conditions):
        result = conditions.get((command, source), None)

        if not result: # Check for substate matches as well (Command.LEVEL, etc)
            if isinstance(command, tuple):
                result = conditions.get((command[0], source), None)
        return result

    def _match_condition_item(self, item):
        if not item:
            return None

        start = item.get(Attribute.START, None)
        if start:
            end = item.get(Attribute.END, None)
            if end:
                now = datetime.now()
                result = crontime_in_range(now, start, end)
                self._logger.debug("Compare times: Now -> {0}  -  Start-> {1}  -  End {2}".format(now,start,end))
                return result
        return item

    def trigger(self, *args, **kwargs):
        commands = kwargs.get(Attribute.COMMAND, None)
        sources = kwargs.get(Attribute.SOURCE, None)
        mapped = kwargs.get(Attribute.MAPPED, None)
        secs = kwargs.get(Attribute.SECS, None)
        start = kwargs.get(Attribute.START, None)
        end = kwargs.get(Attribute.END, None)

        if not isinstance(commands, tuple):
            commands = (commands, )
        if not isinstance(sources, tuple):
            sources = (sources, )

        for command in commands:
            for source in sources:
                m = None
                if not mapped:
                    m = command
                else:
                    m = mapped
                self._triggers.update({(command, source): {Attribute.SECS: secs,
                                                           Attribute.MAPPED: m,
                                                           Attribute.START: CronTimer.to_cron(start),
                                                           Attribute.END: CronTimer.to_cron(end),
                                                           }})

#        timer = CTimer()
#        timer.interval=secs
#        timer.action(self.command, (mapped, ), source=self, original=source)
#        self._triggers.append({'command': command, 'mapped': mapped, 'source': source, 'secs': secs, 'timer': timer})

    def _trigger_start(self, command, source, *args, **kwargs):
        original_command = kwargs.get('original', None)
        trigger = self._triggers.get((command, source), None)
        if not trigger and original_command:
            trigger = self._triggers.get((original_command, source), None)
        if not trigger:
            trigger = self._triggers.get((command, None), None)
        if not trigger:
            trigger = self._triggers.get((original_command, None), None)
##       trigger = self._match_condition(command, source, self._triggers)

        if trigger and self._match_condition_item(trigger):
            timer = self._trigger_timers.get(trigger[Attribute.MAPPED], None)
            if not timer:
                timer = CTimer()
            timer.stop()
            if trigger[Attribute.SECS] > 0:
                timer.action(self.command, (trigger[Attribute.MAPPED], ), source=self, original=source)
                timer.interval = trigger[Attribute.SECS]
                self._trigger_timers.update({trigger[Attribute.MAPPED]: timer} )
                timer.start()
                self._logger.debug('{name} command "{command}" from source "{source}" trigger started, mapped to "{mapped}" waiting {secs} secs. '.format(
                                                                                      name=self.name,
                                                                                      source=source.name if source else None,
                                                                                      command=command,
                                                                                      mapped=trigger[Attribute.MAPPED],
                                                                                      secs=trigger[Attribute.SECS],
                                                                                ))


#        for trigger in self._triggers:
#            if trigger['command'] == command and \
#            (not trigger['source'] or trigger['source'] == source):
#                trigger['timer'].action(self.command, (trigger['mapped'], ), source=self, original=source)
#                trigger['timer'].start()

    def _initial_from_devices(self, *args, **kwargs):
        state = None
        if self.state == State.UNKNOWN:
            for device in self._devices:
#                state = device.state
                (state, command) =  self._command_state_map(device.last_command)
                if state:
                    self.initial(device)
                    self._logger.debug("{name} initial for {command} from {state}".format(
                                                                                        name=self.name,
                                                                                        command=command,
                                                                                        state=state,
                                                                                        ));
        return

    @property
    def last_command(self):
        return self._previous_command

    def changes_only(self, value):
        self._changes_only=value
        return self._changes_only

    def retrigger_delay(self, *args, **kwargs):
        secs = kwargs.get('secs', None)
        self._retrigger_delay = CTimer()
        self._retrigger_delay.interval = secs

    def _filter_retrigger_delay(self, *args, **kwargs):
        """
        If there is a need to squelch multiple of the same command within a certain timeframe
        """
        original_state = kwargs.get('original_state', None)
        new_state = kwargs.get('new_state', None)
        if new_state == original_state and self._retrigger_delay and self._retrigger_delay.isAlive():
            return True
        elif new_state != original_state and self._retrigger_delay:
            self._retrigger_delay.restart()
        return False

    def onStateChanged(self, func):
        self._delegates_state_change.append(func)
        return True

    @staticmethod
    def onStateChangedGlobal(func):
        StateDevice._delegates_state_change.append(func)
        return True

    @staticmethod
    def dump_garbage():
        """
        show us what's the garbage about
        """
        c=-1
        # force collection
    #    print "\nGARBAGE:"
        gc.collect()

    #    print "\nGARBAGE OBJECTS:"
#        for x in gc.garbage:
#            s = str(x)
#            if len(s) > 80: s = s[:80]
#            print type(x),"\n  ", s
        try:
            c = len(gc.garbage)
        except:
            pass
        return c
Beispiel #17
0
 def retrigger_delay(self, *args, **kwargs):
     secs = kwargs.get('secs', None)
     self._retrigger_delay = CTimer()
     self._retrigger_delay.interval = secs