00001 """
00002 zigbee.py
00003
00004 By Greg Rapp, 2010
00005 Inspired by code written by Paul Malmsten, 2010
00006 Inspired by code written by Amit Synderman and Marco Sangalli
00007 gdrapp@gmail.com
00008
00009 This module implements an XBee ZB (ZigBee) API library.
00010 """
00011 import struct
00012 from xbee.base import XBeeBase
00013
00014 class ZigBee(XBeeBase):
00015 """
00016 Provides an implementation of the XBee API for XBee ZB (ZigBee) modules
00017 with recent firmware.
00018
00019 Commands may be sent to a device by instantiating this class with
00020 a serial port object (see PySerial) and then calling the send
00021 method with the proper information specified by the API. Data may
00022 be read from a device synchronously by calling wait_read_frame.
00023 For asynchronous reads, see the defintion of XBeeBase.
00024 """
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 api_commands = {"at":
00035 [{'name':'id', 'len':1, 'default':'\x08'},
00036 {'name':'frame_id', 'len':1, 'default':'\x01'},
00037 {'name':'command', 'len':2, 'default':None},
00038 {'name':'parameter', 'len':None, 'default':None}],
00039 "queued_at":
00040 [{'name':'id', 'len':1, 'default':'\x09'},
00041 {'name':'frame_id', 'len':1, 'default':'\x01'},
00042 {'name':'command', 'len':2, 'default':None},
00043 {'name':'parameter', 'len':None, 'default':None}],
00044 "remote_at":
00045 [{'name':'id', 'len':1, 'default':'\x17'},
00046 {'name':'frame_id', 'len':1, 'default':'\x00'},
00047
00048 {'name':'dest_addr_long', 'len':8, 'default':struct.pack('>Q', 0)},
00049 {'name':'dest_addr', 'len':2, 'default':'\xFF\xFE'},
00050 {'name':'options', 'len':1, 'default':'\x02'},
00051 {'name':'command', 'len':2, 'default':None},
00052 {'name':'parameter', 'len':None, 'default':None}],
00053 "tx":
00054 [{'name':'id', 'len':1, 'default':'\x10'},
00055 {'name':'frame_id', 'len':1, 'default':'\x01'},
00056 {'name':'dest_addr_long', 'len':8, 'default':None},
00057 {'name':'dest_addr', 'len':2, 'default':None},
00058 {'name':'broadcast_radius','len':1, 'default':'\x00'},
00059 {'name':'options', 'len':1, 'default':'\x00'},
00060 {'name':'data', 'len':None, 'default':None}],
00061 "tx_explicit":
00062 [{'name':'id', 'len':1, 'default':'\x11'},
00063 {'name':'frame_id', 'len':1, 'default':'\x00'},
00064 {'name':'dest_addr_long', 'len':8, 'default':None},
00065 {'name':'dest_addr', 'len':2, 'default':None},
00066 {'name':'src_endpoint', 'len':1, 'default':None},
00067 {'name':'dest_endpoint', 'len':1, 'default':None},
00068 {'name':'cluster', 'len':1, 'default':None},
00069 {'name':'profile', 'len':1, 'default':None},
00070 {'name':'broadcast_radius','len':1, 'default':'\x00'},
00071 {'name':'options', 'len':1, 'default':'\x00'},
00072 {'name':'data', 'len':None, 'default':None}]
00073 }
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 api_responses = {"\x90":
00090 {'name':'rx',
00091 'structure':
00092 [{'name':'source_addr_long','len':8},
00093 {'name':'source_addr', 'len':2},
00094 {'name':'options', 'len':1},
00095 {'name':'rf_data', 'len':None}]},
00096 "\x91":
00097 {'name':'rx_explicit',
00098 'structure':
00099 [{'name':'source_addr_long','len':8},
00100 {'name':'source_addr', 'len':2},
00101 {'name':'source_endpoint', 'len':1},
00102 {'name':'dest_endpoint', 'len':1},
00103 {'name':'cluster', 'len':2},
00104 {'name':'profile', 'len':2},
00105 {'name':'options', 'len':1},
00106 {'name':'rf_data', 'len':None}]},
00107 "\x92":
00108 {'name':'rx_io_data_long_addr',
00109 'structure':
00110 [{'name':'source_addr_long','len':8},
00111 {'name':'source_addr', 'len':2},
00112 {'name':'options', 'len':1},
00113 {'name':'samples', 'len':None}],
00114 'parse_as_io_samples':'samples'},
00115 "\x8b":
00116 {'name':'tx_status',
00117 'structure':
00118 [{'name':'frame_id', 'len':1},
00119 {'name':'dest_addr', 'len':2},
00120 {'name':'retries', 'len':1},
00121 {'name':'deliver_status', 'len':1},
00122 {'name':'discover_status', 'len':1}]},
00123 "\x8a":
00124 {'name':'status',
00125 'structure':
00126 [{'name':'status', 'len':1}]},
00127 "\x88":
00128 {'name':'at_response',
00129 'structure':
00130 [{'name':'frame_id', 'len':1},
00131 {'name':'command', 'len':2},
00132 {'name':'status', 'len':1},
00133 {'name':'parameter', 'len':None}]},
00134 "\x97":
00135 {'name':'remote_at_response',
00136 'structure':
00137 [{'name':'frame_id', 'len':1},
00138 {'name':'source_addr_long','len':8},
00139 {'name':'source_addr', 'len':2},
00140 {'name':'command', 'len':2},
00141 {'name':'status', 'len':1},
00142 {'name':'parameter', 'len':None}]},
00143 "\x95":
00144 {'name':'node_id_indicator',
00145 'structure':
00146 [{'name':'sender_addr_long','len':8},
00147 {'name':'sender_addr', 'len':2},
00148 {'name':'options', 'len':1},
00149 {'name':'source_addr', 'len':2},
00150 {'name':'source_addr_long','len':8},
00151 {'name':'node_id', 'len':'null_terminated'},
00152 {'name':'parent_source_addr','len':2},
00153 {'name':'device_type', 'len':1},
00154 {'name':'source_event', 'len':1},
00155 {'name':'digi_profile_id', 'len':2},
00156 {'name':'manufacturer_id', 'len':2}]}
00157 }
00158
00159 def __init__(self, *args, **kwargs):
00160
00161 super(ZigBee, self).__init__(*args, **kwargs)
00162
00163 def _parse_samples_header(self, io_bytes):
00164 """
00165 _parse_samples_header: binary data in XBee ZB IO data format ->
00166 (int, [int ...], [int ...], int, int)
00167
00168 _parse_samples_header will read the first three bytes of the
00169 binary data given and will return the number of samples which
00170 follow, a list of enabled digital inputs, a list of enabled
00171 analog inputs, the dio_mask, and the size of the header in bytes
00172
00173 _parse_samples_header is overloaded here to support the additional
00174 IO lines offered by the XBee ZB
00175 """
00176 header_size = 4
00177
00178
00179 sample_count = ord(io_bytes[0])
00180
00181
00182 dio_mask = (ord(io_bytes[1]) << 8 | ord(io_bytes[2])) & 0x0E7F
00183
00184
00185 aio_mask = ord(io_bytes[3])
00186
00187
00188 dio_chans = []
00189 aio_chans = []
00190
00191 for i in range(0,13):
00192 if dio_mask & (1 << i):
00193 dio_chans.append(i)
00194
00195 dio_chans.sort()
00196
00197 for i in range(0,8):
00198 if aio_mask & (1 << i):
00199 aio_chans.append(i)
00200
00201 aio_chans.sort()
00202
00203 return (sample_count, dio_chans, aio_chans, dio_mask, header_size)