Beispiel #1
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()
Beispiel #2
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)