Example #1
0
 def __init__(self, refresh_rate=900.0):
     ImmortalThread.__init__(self, name='RAFD')
     if debug: print 'init RAFD Server '
     self.semaphore = Semaphore(0)
     self._scheduler = scheduler
     self.refresh_rate = refresh_rate
     scheduler.seconds_from_now_do(30.0, self._tick)
Example #2
0
 def __init__(self, refresh_rate=900.0):
     ImmortalThread.__init__(self, name='RAFD')
     if debug: print 'init RAFD Server '
     self.semaphore = Semaphore(0)
     self._scheduler = scheduler
     self.refresh_rate = refresh_rate
     scheduler.seconds_from_now_do(30.0, self._tick)
Example #3
0
 def __init__(self, group=None, target=None, name=None, args=(),
              kwargs=None, verbose=None, *vargs, **keywords):
     ImmortalThread.__init__(self, group, target, name, args, kwargs,
                             verbose, *vargs, **keywords)
     self.__one_time_init()
     self.__every_time_init()
     return
Example #4
0
 def __init__(self, node):
     node_url = as_node_url(node)
     ImmortalThread.__init__(self, name='_WhoIsThread(%r)' % node_url)
     self.node = node
     self.debug = node.debug
     self.discover_interval = node.discover_interval
     if self.debug:
         print '%s.__init__()' % self.getName()
Example #5
0
 def start(self):
     if not self._monitor.is_running():
         self._monitor.start_monitor()
     self._running = True
     self._synchronous_transaction = Transaction(self, None, self._bump_cv)
     self._synchronous_transaction.set_timeout(self.timeout)
     ImmortalThread.start(self)
     return
Example #6
0
 def __init__(self):
     ImmortalThread.__init__(self, name="Redusa Async Core")
     self._use_poll = 0
     try:
         select.poll()
         self._use_poll = 1
     except:
         msglog.log('broadway', msglog.types.INFO,
                    'Platform does not support poll().')
     return
Example #7
0
 def __init__(self):
     ImmortalThread.__init__(self, name="Redusa Async Core")
     self._use_poll = 0
     try:
         select.poll()
         self._use_poll = 1
     except:
         msglog.log('broadway', msglog.types.INFO,
                    'Platform does not support poll().')
     return
Example #8
0
 def start(self):
     if not self.__running:
         known_alarms = self.get_child('alarms')
         for child in known_alarms.children_nodes():
             child.event_subscribe(self, AlarmTriggerEvent)
             child.event_subscribe(self, AlarmClearEvent)
         self.__running = 1
         self.__thread = ImmortalThread(name=self.name, target=self.__run)
         self.__thread.start()
         ServiceNode.start(self)
     return
Example #9
0
 def __init__(self, timeout=2.0):
     self.timeout = timeout
     self.stations = {}
     self._monitor = monitor.ChannelMonitor(self.timeout)
     self.tm_number = self.tm_counter.increment()
     self._response_tp = ThreadPool(1, 'Jace Response Pool')
     self._pending_responses = Queue()
     self._callbacks = {}
     self._running = False
     self._sync_get_lock = Lock()
     self._last_sync_get = uptime.secs()
     self._cv = Condition()
     ImmortalThread.__init__(self, None, None, 'Jace Transaction Manager')
     return
Example #10
0
 def __init__(self,
              group=None,
              target=None,
              name=None,
              args=(),
              kwargs=None,
              verbose=None,
              *vargs,
              **keywords):
     ImmortalThread.__init__(self, group, target, name, args, kwargs,
                             verbose, *vargs, **keywords)
     self.__one_time_init()
     self.__every_time_init()
     return
Example #11
0
 def __init__(self, timeout=2.0):
     self.timeout = timeout
     self.stations = {}
     self._monitor = monitor.ChannelMonitor(self.timeout)
     self.tm_number = self.tm_counter.increment()
     self._response_tp = ThreadPool(1, 'Jace Response Pool')
     self._pending_responses = Queue()
     self._callbacks = {}
     self._running = False
     self._sync_get_lock = Lock()
     self._last_sync_get = uptime.secs()
     self._cv = Condition()
     ImmortalThread.__init__(self, None, None,
                             'Jace Transaction Manager')
     return
Example #12
0
    def __init__(self, owner):
        self._buffer = None
        self.owner = owner  # owner is the slave ion
        self._port = owner.parent  # slave ion is child of com port
        ImmortalThread.__init__(self, name="%s(%r)" % (self.__class__, self._port.dev))
        # Determine if we are attached to an RS485 port on a Megatron
        # If so then we need to handle reception of our transmitted data
        self.megatron = properties.HARDWARE_CODENAME == "Megatron"
        self.megatron_485 = self.megatron and (self.owner.parent.name in ("com3", "com4", "com5", "com6"))
        print "Modbus Slave device thread started in",
        if self.megatron_485:
            print "RS485 echo mode"
        else:
            print "RS485 no-echo mode"

        return
Example #13
0
 def start(self):
     if self.running is False:
         self.endpoint = XCommandIface(
             self.rpc_port, 
             self.address, 
             self.debug
         )
         self.lh = SerialFramerIface(
             self.serial_forwarder_port, 
             self.address, 
             self.debug
         )
         self._thread = ImmortalThread(target=self._run, args=())
         self.running = True
         self._thread.start()
     super(Gateway, self).start()
Example #14
0
 def _startmanager(self):
     self._queue = queue = Queue()
     self._stopflag = stopflag = Flag()
     self._thread = thread = ImmortalThread(name=self.url,
                                            target=self._runmanager,
                                            args=(stopflag, queue))
     thread.start()
Example #15
0
 def start(self):
     if not self.__running:
         known_alarms = self.get_child('alarms')
         for child in known_alarms.children_nodes():
             child.event_subscribe(self, AlarmTriggerEvent)
             child.event_subscribe(self, AlarmClearEvent)
         self.__running = 1
         self.__thread = ImmortalThread(name=self.name,target=self.__run)
         self.__thread.start()
         ServiceNode.start(self)
     return
Example #16
0
    def __init__(self, owner):
        self._buffer = None
        self.owner = owner  #owner is the slave ion
        self._port = owner.parent  #slave ion is child of com port
        ImmortalThread.__init__(self,
                                name="%s(%r)" %
                                (self.__class__, self._port.dev))
        # Determine if we are attached to an RS485 port on a Megatron
        # If so then we need to handle reception of our transmitted data
        self.megatron = properties.HARDWARE_CODENAME == 'Megatron'
        self.megatron_485 = self.megatron and (self.owner.parent.name
                                               in ('com3', 'com4', 'com5',
                                                   'com6'))
        print 'Modbus Slave device thread started in',
        if self.megatron_485:
            print 'RS485 echo mode'
        else:
            print 'RS485 no-echo mode'

        return
Example #17
0
 def __init__(self, vcp):
     #super(TcpTunnel, self).__init__(self)
     ImmortalThread.__init__(self)
     self._needs_reconfig = 0
     # link to the port object
     self._vcp = vcp
     self._lock = Lock()
     self._op_lock = Lock()
     # list of operations to apply to an {out|in}bound tcp 
     # segment. In the future this might include operations 
     # such as encryption or compression - for now, only the
     # header info. that is applied by devices such as 
     # Lantronix's UDS-10 is supported.  Up refers to what is
     # being applied to outbound tcp data, down to received.
     # Methods should be added to these lists in the order they
     # are to be applied.
     self._up_segment_ops = []
     self._down_segment_ops = []
     # transaction identifier - used only in vcp mode.
     self.__tid = 0
     self._pending_tid = 0
     # one and only poll object.  socket, serial fd and
     # command pipe are all polled.
     self._poll_obj = None
     # command pipe allows other threads to insert control
     # messages.
     self._cmd_pipe = None
     # both sides (serial & socket) of the tunnel
     self._sock_listen_fd = None
     self._sock_fd = None
     self._serial_port = None
     # tcp state management
     self._is_connected = 0
     self.is_active = 0
     # tunnel statistics
     self.tcp_bytes_rcvd = 0
     self.tcp_bytes_sent = 0
     self.serial_bytes_rcvd = 0
     self.serial_bytes_sent = 0
     self.connection_attempts = 0
Example #18
0
 def __init__(self, connection):
     ImmortalThread.__init__(self, name='MODBUS')
     self.connection = connection
     if debug: print 'init MODBUS Server '
     self.debug = debug
     self.ready = 0
Example #19
0
class AlarmManager(ServiceNode, EventConsumerMixin):
    def __init__(self):
        ServiceNode.__init__(self)
        EventConsumerMixin.__init__(self, self.handle_alarm, \
                                    self.handle_exception)
        self.__running = 0
        self.__queue = Queue()
        self._id_manager = None
        self._dynamic_alarms = {}
        return

    def configure(self, config):
        ServiceNode.configure(self, config)
        if self._id_manager is None:
            self._id_manager = UniqueID(self)
        return

    def unique_id(self):
        return self._id_manager.allocate_id()

    def active(self):
        children = []
        for child in self.chidlren_nodes():
            if child.active():
                children.append(child)
        return children

    def acknowledged(self):
        children = []
        for child in self.children_nodes():
            if child.active() and child.acknowledged():
                children.append(child)
        return children

    def unacknowledged(self):
        children = []
        for child in self.children_nodes():
            if child.active() and not child.acknowledged():
                children.append(child)
        return children

    def add_dynamic_alarm(self, dynamic_alarm):
        dynamic_alarm_id = id(dynamic_alarm)
        if not self._dynamic_alarms.has_key(dynamic_alarm_id):
            self._dynamic_alarms[dynamic_alarm_id] = dynamic_alarm
            dynamic_alarm.event_subscribe(self, AlarmTriggerEvent)
            dynamic_alarm.event_subscribe(self, AlarmClearEvent)
        return

    def start(self):
        if not self.__running:
            known_alarms = self.get_child('alarms')
            for child in known_alarms.children_nodes():
                child.event_subscribe(self, AlarmTriggerEvent)
                child.event_subscribe(self, AlarmClearEvent)
            self.__running = 1
            self.__thread = ImmortalThread(name=self.name, target=self.__run)
            self.__thread.start()
            ServiceNode.start(self)
        return

    def stop(self):
        self.__running = 0
        self.__thread = None
        self.__queue.put(None)
        try:
            # @fixme try/except/else is a hack to survive testcases use
            #        of prune.  Really should revisit...
            alarms = self.get_child('alarms')
        except:
            pass
        else:
            for child in alarms.children_nodes():
                child.cancel(self, AlarmTriggerEvent)
                child.cancel(self, AlarmClearEvent)
        return ServiceNode.stop(self)

    def queue_alarm_check(self, alarm):
        self.__queue.put(alarm)

    def __run(self):
        while self.__running:
            alarm = self.__queue.get()
            if alarm:
                alarm.check_condition()
        else:
            self.__thread.should_die()
        return

    ##
    # Both AlarmClear and AlarmTrigger events come here,
    # separate the two and send to the appropriate handler.
    def handle_alarm(self, alarm):
        alarm.source.caught(alarm)
        if self.debug:
            msglog.log('broadway', msglog.types.ALARM, str(alarm))
        if alarm.__class__ == AlarmTriggerEvent:
            return self._handle_trigger(alarm)
        elif alarm.__class__ == AlarmClearEvent:
            return self._handle_clear(alarm)
        msglog.log(
            'broadway', msglog.types.WARN, 'Alarm manager %s got ' +
            'an event that it does not recoginize, ignoring.' % self.name)

    def _handle_trigger(self, alarm):
        export_thread = Thread(name=alarm.source.name,
                               target=self._run_exports,
                               args=(alarm, ))
        export_thread.start()

    def _run_exports(self, alarm):
        if self.has_child('exporters'):
            exporters = self.get_child('exporters').children_nodes()
            exceptions = []
            for exporter in exporters:
                try:
                    exporter.export(alarm)
                except Exception, e:
                    msglog.exception()
                    exceptions.append(e)
            if exceptions:
                alarm.source.fail(alarm)
            else:
                alarm.source.success(alarm)
        else:
Example #20
0
class AlarmManager(ServiceNode,EventConsumerMixin):
    def __init__(self):
        ServiceNode.__init__(self)
        EventConsumerMixin.__init__(self, self.handle_alarm, \
                                    self.handle_exception)
        self.__running = 0
        self.__queue = Queue()
        self._id_manager = None
        self._dynamic_alarms = {}
        return
    def configure(self, config):
        ServiceNode.configure(self, config)
        if self._id_manager is None:
            self._id_manager = UniqueID(self)
        return
    def unique_id(self):
        return self._id_manager.allocate_id()
    def active(self):
        children = []
        for child in self.chidlren_nodes():
            if child.active():
                children.append(child)
        return children
    def acknowledged(self):
        children = []
        for child in self.children_nodes():
            if child.active() and child.acknowledged():
                children.append(child)
        return children
    def unacknowledged(self):
        children = []
        for child in self.children_nodes():
            if child.active() and not child.acknowledged():
                children.append(child)
        return children
    def add_dynamic_alarm(self, dynamic_alarm):
        dynamic_alarm_id = id(dynamic_alarm)
        if not self._dynamic_alarms.has_key(dynamic_alarm_id):
            self._dynamic_alarms[dynamic_alarm_id] = dynamic_alarm
            dynamic_alarm.event_subscribe(self, AlarmTriggerEvent)
            dynamic_alarm.event_subscribe(self, AlarmClearEvent)
        return
    def start(self):
        if not self.__running:
            known_alarms = self.get_child('alarms')
            for child in known_alarms.children_nodes():
                child.event_subscribe(self, AlarmTriggerEvent)
                child.event_subscribe(self, AlarmClearEvent)
            self.__running = 1
            self.__thread = ImmortalThread(name=self.name,target=self.__run)
            self.__thread.start()
            ServiceNode.start(self)
        return
    def stop(self):
        self.__running = 0
        self.__thread = None
        self.__queue.put(None)
        try:
            # @fixme try/except/else is a hack to survive testcases use
            #        of prune.  Really should revisit...
            alarms = self.get_child('alarms')
        except:
            pass
        else:
            for child in alarms.children_nodes():
                child.cancel(self, AlarmTriggerEvent)
                child.cancel(self, AlarmClearEvent)
        return ServiceNode.stop(self)
    def queue_alarm_check(self,alarm):
        self.__queue.put(alarm)
    def __run(self):
        while self.__running:
            alarm = self.__queue.get()
            if alarm:
                alarm.check_condition()
        else:
            self.__thread.should_die()
        return
    ##
    # Both AlarmClear and AlarmTrigger events come here,
    # separate the two and send to the appropriate handler.
    def handle_alarm(self, alarm):
        alarm.source.caught(alarm)
        if self.debug:
            msglog.log('broadway',msglog.types.ALARM,str(alarm))
        if alarm.__class__ == AlarmTriggerEvent:
            return self._handle_trigger(alarm)
        elif alarm.__class__ == AlarmClearEvent:
            return self._handle_clear(alarm)
        msglog.log('broadway', msglog.types.WARN,'Alarm manager %s got ' +
                   'an event that it does not recoginize, ignoring.' % 
                   self.name)
    def _handle_trigger(self, alarm):
        export_thread = Thread(name=alarm.source.name,
                               target=self._run_exports,
                               args=(alarm,))
        export_thread.start()
    def _run_exports(self,alarm):
        if self.has_child('exporters'):
            exporters = self.get_child('exporters').children_nodes()
            exceptions = []
            for exporter in exporters:
                try:
                    exporter.export(alarm)
                except Exception, e:
                    msglog.exception()
                    exceptions.append(e)
            if exceptions:
                alarm.source.fail(alarm)
            else:
                alarm.source.success(alarm)
        else:
Example #21
0
 def __init__(self, queue):
     ImmortalThread.__init__(self, name='BBMD')
     self.queue = queue
     if debug: print 'init BBMD Server '
Example #22
0
 def __init__(self, queue):
     ImmortalThread.__init__(self, name='BBMD')
     self.queue = queue
     if debug: print 'init BBMD Server '
Example #23
0
class Gateway(CompositeNode):
    def __init__(self):
        self.lightpoints = {}
        self.running = False
        self.endpoint = None
        self.lh = None
        self._seq_num = 1
        self._sched_lock = Lock()
        super(Gateway, self).__init__()
        
    def configure(self, cd):
        super(Gateway, self).configure(cd)
        set_attribute(self, 'address', REQUIRED, cd)
        set_attribute(self, 'debug', 1, cd, int)
        set_attribute(self, 'net_grp_id', 125, cd, int) # network net_grp_id
        set_attribute(self, 'rpc_port', 9003, cd, int)
        set_attribute(self, 'serial_forwarder_port', 9001, cd, int)
        if self.running is True:
            # force endpoint reconfiguration
            if self.endpoint and self.endpoint.connection_ok():
                self.endpoint.close_connection()
            self.running = False 
            self.start() 
        
    def configuration(self):
        cd = super(Gateway, self).configuration()
        get_attribute(self, 'address', cd)
        get_attribute(self, 'debug', cd)
        get_attribute(self, 'net_grp_id', cd)
        get_attribute(self, 'rpc_port', cd)
        get_attribute(self, 'serial_forwarder_port', cd)
        return cd
        
    def start(self):
        if self.running is False:
            self.endpoint = XCommandIface(
                self.rpc_port, 
                self.address, 
                self.debug
            )
            self.lh = SerialFramerIface(
                self.serial_forwarder_port, 
                self.address, 
                self.debug
            )
            self._thread = ImmortalThread(target=self._run, args=())
            self.running = True
            self._thread.start()
        super(Gateway, self).start()
        
    def stop(self):
        self.running = False
        
    def _run(self):
        while self.running:
            # initialize the communications interface
            try:
                msglog.log('Adura', INFO, 'Initializing XServer SerialFramer connection.')
                self.lh.open_connection()
                msglog.log('Adura', INFO, 'XServer connection succeeded.')
            except EResourceError:
                self.stop()
                msglog.log('Adura', ERR, 'Failed to connect to XServer.')
                raise
            except (EConnectionError, ETimeout):
                msglog.log('Adura', WARN, 'Failed to connect to XServer - attempting to reconnect.') 
                pass
            except:
                # unaccounted for exception - send it to the msglog
                msglog.log('Adura', ERR, 'Failed to connect to XServer.')
                msglog.exception()
            while self.lh.connection_ok():
                msg = None
                try:
                    msg = self.lh.get_next_msg()
                except:
                    # connection will now be closed, re-init comm.
                    msglog.exception(prefix='Ignored')
                if msg:
                    frame = AduraFrame(msg)
                    if self.debug:
                        msglog.log('Adura', INFO, frame.get_value_string())
                    if frame.frame_type == 'lightpoint_data_config':
                        lp = self.lightpoints.get(frame.get('firmware_id'))
                        if lp is not None:
                            lp.update(frame)
            time.sleep(30) #pause before attempting to reconnect
        else: 
            if self.lh.connection_ok():
                self.lh.close_connection()
            self._thread.should_die()
        return
        
    def register(self, lightpoint):
        self.lightpoints[lightpoint.lightpoint_id] = lightpoint
        
    def _check_cov_timeout(self):
        for lp in self.lightpoints:
            if (uptime.secs() - lp._last_update) > lp.group.ttl:
                # generate an ETimeout for the benefit of event consumers
                lp.update({'relayState1':ETimeout()})
        
    def _get_next_seq(self):
        seq = self._seq_num
        self._seq_num += 1
        if self._seq_num >= 0xffff:
            self._seq_num = 1
        return seq
        
    # LightLogic XML-RPC methods follow.  Note, HTTP is *not* used
    # for transport.  It's just a raw socket that begins with a 4-byte 
    # length field followed by xml-rpc based payload.
    
    ##
    # The control command for an individual LightPoint
    # 
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network (125)
    # @param actDevice  The id of the relay (5)
    # @actState  The action (0 = OFF, 1 = ON)
    #
    def actuate(self, dest, act_device, act_state):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':act_state}
        if act_device == 12:
            # broadcast to all lightpoints - requires additional param 
            # that is a 16 bit incrementing seq number.
            params['seqNumber'] = self._get_next_seq()
        return self.endpoint.write('xmesh.actuate', params)
            
    ##
    # Clears previous schedule events
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actState  1536
    # @param seqNumber  Unique sequence number for the specific command
    #
    def adura_clear_schedule(self, dest=ADURA_ALL_LIGHT_POINTS_ADDR):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actState':ADURA_CLR_SCHED,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('xmesh.adura_clear_schedule', params)
        
    # The following 5 messages are repeated n times, depending on how many
    # schedule events are required.
    
    ##
    # Sets the day of the schedule event (currently 10 - all days)
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  Day ID (10) 
    # @param actState  1792
    # @param seqNumber  Unique sequence number for the specific command
    #
    def adura_setschedule_day(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_DAY,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_day', params)
        
    ##
    # Sets the number of hours relative to sunrise
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  The hours 0 - 23 or
    # Sunrise == hour + 128 = number of hours after sunrise
    #            hour + 160 = number of hours before sunrise
    # Sunset == hour + 64 = number of hourse after sunset
    #           hour + 96 = number of hours before sunset
    # @param  actState  2304
    # @param  seqNumber  Unique sequence number for the specific command 
    def adura_setschedule_hour(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_HOUR,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_hour', params)
                                                    
    ##
    # Sets the number of minutes relative to adura_setschedule_hour
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  Minutes (0-60).  Added to hour above
    # @param actState  2560
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setschedule_minute(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_MIN,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_minute', params)
                                                      
    ##
    # Sets the lighting group that the schedule controls.  Currently that is limited to the 
    # "All LightPoints group (12).
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  The lighting group to actuate
    # @param actState  2816
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setschedule_group(self, dest):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':ADURA_ALL_LIGHT_POINTS_GRP,
                  'actState':ADURA_SET_SCHED_GROUP,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_group', params)
                                                     
    ##
    # Sets the action for the schedule event and initiates the schedule event
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  0 = Off, 1 = On
    # @param actState  2048
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setschedule_action(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_ACTION,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_group', params)
                                                              
    ##
    # Sets the current date and time.  Date and time are stored in a 32-bit format.
    # Two messages must be sent, the first to set the high word, the latter the low word.
    # 
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  DateTime value (16bit value)
    # @param actState  Which word to set, 0x0502 == high, 0x0501 == low
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setdatetime(self, dest, act_device, act_state):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':act_state,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setdatetime', params)