12
12
import zigpy .types
13
13
import zigpy .util
14
14
from zigpy .zcl .clusters .general import Groups
15
- from zigpy .zdo .types import NodeDescriptor , ZDOCmd
15
+ from zigpy .zdo .types import LogicalType , NodeDescriptor , ZDOCmd
16
16
17
17
import zigpy_xbee .api
18
18
from zigpy_xbee .config import CONF_DEVICE , CONFIG_SCHEMA , SCHEMA_DEVICE
@@ -40,42 +40,32 @@ class ControllerApplication(zigpy.application.ControllerApplication):
40
40
def __init__ (self , config : Dict [str , Any ]):
41
41
super ().__init__ (config = zigpy .config .ZIGPY_SCHEMA (config ))
42
42
self ._api : Optional [zigpy_xbee .api .XBee ] = None
43
- self ._nwk = 0
44
43
45
- async def shutdown (self ):
44
+ async def disconnect (self ):
46
45
"""Shutdown application."""
47
46
if self ._api :
48
47
self ._api .close ()
49
48
50
- async def startup (self , auto_form = False ):
51
- """Perform a complete application startup"""
49
+ async def connect (self ):
52
50
self ._api = await zigpy_xbee .api .XBee .new (self , self ._config [CONF_DEVICE ])
53
51
try :
54
52
# Ensure we have escaped commands
55
53
await self ._api ._at_command ("AP" , 2 )
56
54
except asyncio .TimeoutError :
57
55
LOGGER .debug ("No response to API frame. Configure API mode" )
58
56
if not await self ._api .init_api_mode ():
59
- LOGGER .error ("Failed to configure XBee API mode." )
60
- return False
61
-
62
- await self ._api ._at_command ("AO" , 0x03 )
63
-
64
- serial_high = await self ._api ._at_command ("SH" )
65
- serial_low = await self ._api ._at_command ("SL" )
66
- ieee = EUI64 .deserialize (
67
- serial_high .to_bytes (4 , "big" ) + serial_low .to_bytes (4 , "big" )
68
- )[0 ]
69
- self ._ieee = zigpy .types .EUI64 (ieee )
70
- LOGGER .debug ("Read local IEEE address as %s" , self ._ieee )
57
+ raise zigpy .exceptions .ControllerException (
58
+ "Failed to configure XBee API mode."
59
+ )
71
60
61
+ async def start_network (self ):
72
62
try :
73
63
association_state = await asyncio .wait_for (
74
64
self ._get_association_state (), timeout = 4
75
65
)
76
66
except asyncio .TimeoutError :
77
67
association_state = 0xFF
78
- self . _nwk = await self . _api . _at_command ( "MY" )
68
+
79
69
enc_enabled = await self ._api ._at_command ("EE" )
80
70
enc_options = await self ._api ._at_command ("EO" )
81
71
zb_profile = await self ._api ._at_command ("ZS" )
@@ -85,62 +75,83 @@ async def startup(self, auto_form=False):
85
75
enc_options != 2 ,
86
76
zb_profile != 2 ,
87
77
association_state != 0 ,
88
- self ._nwk != 0 ,
78
+ self .state . node_info . nwk != 0x0000 ,
89
79
)
90
- if auto_form and any (should_form ):
91
- await self .form_network ()
92
80
81
+ if should_form :
82
+ raise zigpy .exceptions .NetworkNotFormed ("Network is not formed" )
83
+
84
+ # Disable joins
93
85
await self ._api ._at_command ("NJ" , 0 )
94
86
await self ._api ._at_command ("SP" , CONF_CYCLIC_SLEEP_PERIOD )
95
87
await self ._api ._at_command ("SN" , CONF_POLL_TIMEOUT )
96
- id = await self ._api ._at_command ("ID" )
97
- LOGGER .debug ("Extended PAN ID: 0x%016x" , id )
98
- id = await self ._api ._at_command ("OP" )
99
- LOGGER .debug ("Operating Extended PAN ID: 0x%016x" , id )
100
- id = await self ._api ._at_command ("OI" )
101
- LOGGER .debug ("PAN ID: 0x%04x" , id )
102
- try :
103
- ce = await self ._api ._at_command ("CE" )
104
- LOGGER .debug ("Coordinator %s" , "enabled" if ce else "disabled" )
105
- except RuntimeError as exc :
106
- LOGGER .debug ("sending CE command: %s" , exc )
107
88
108
- dev = zigpy .device .Device (self , self .ieee , self .nwk )
89
+ dev = zigpy .device .Device (
90
+ self , self .state .node_info .ieee , self .state .node_info .nwk
91
+ )
109
92
dev .status = zigpy .device .Status .ENDPOINTS_INIT
110
93
dev .add_endpoint (XBEE_ENDPOINT_ID )
111
- xbee_dev = XBeeCoordinator (self , self .ieee , self .nwk , dev )
94
+
95
+ xbee_dev = XBeeCoordinator (
96
+ self , self .state .node_info .ieee , self .state .node_info .nwk , dev
97
+ )
112
98
self .listener_event ("raw_device_initialized" , xbee_dev )
113
99
self .devices [dev .ieee ] = xbee_dev
114
100
115
- async def force_remove (self , dev ):
116
- """Forcibly remove device from NCP."""
117
- pass
101
+ async def load_network_info (self , * , load_devices = False ):
102
+ network_info = self .state .network_info
103
+ node_info = self .state .node_info
104
+
105
+ # Load node info
106
+ node_info .nwk = await self ._api ._at_command ("MY" )
107
+ serial_high = await self ._api ._at_command ("SH" )
108
+ serial_low = await self ._api ._at_command ("SL" )
109
+ node_info .ieee = zigpy .types .EUI64 (
110
+ EUI64 .deserialize (
111
+ serial_high .to_bytes (4 , "big" ) + serial_low .to_bytes (4 , "big" )
112
+ )[0 ]
113
+ )
114
+
115
+ if await self ._api ._at_command ("CE" ) == 0x01 :
116
+ node_info .logical_type = LogicalType .Coordinator
117
+ else :
118
+ node_info .logical_type = LogicalType .EndDevice
119
+
120
+ # Load network info
121
+ network_info .pan_id = await self ._api ._at_command ("OI" )
122
+ network_info .extended_pan_id = await self ._api ._at_command ("ID" )
123
+ network_info .channel = await self ._api ._at_command ("CH" )
124
+
125
+ async def write_network_info (self , * , network_info , node_info ):
126
+ scan_bitmask = 1 << (network_info .channel - 11 )
118
127
119
- async def form_network (self , channel = 15 , pan_id = None , extended_pan_id = None ):
120
- LOGGER .info ("Forming network on channel %s" , channel )
121
- scan_bitmask = 1 << (channel - 11 )
122
128
await self ._api ._queued_at ("ZS" , 2 )
123
129
await self ._api ._queued_at ("SC" , scan_bitmask )
124
130
await self ._api ._queued_at ("EE" , 1 )
125
131
await self ._api ._queued_at ("EO" , 2 )
126
- await self ._api ._queued_at ("NK" , 0 )
127
- await self ._api ._queued_at ("KY" , b"ZigBeeAlliance09" )
132
+
133
+ key_as_int = int .from_bytes (network_info .network_key .key .serialize (), "big" )
134
+ await self ._api ._queued_at ("NK" , key_as_int )
135
+
136
+ tclk_as_int = int .from_bytes (network_info .tc_link_key .key .serialize (), "big" )
137
+ await self ._api ._queued_at ("KY" , tclk_as_int )
138
+
128
139
await self ._api ._queued_at ("NJ" , 0 )
129
140
await self ._api ._queued_at ("SP" , CONF_CYCLIC_SLEEP_PERIOD )
130
141
await self ._api ._queued_at ("SN" , CONF_POLL_TIMEOUT )
131
- try :
132
- await self ._api ._queued_at ("CE" , 1 )
133
- except RuntimeError :
134
- pass
142
+ await self ._api ._queued_at ("SM" , 0 )
143
+ await self ._api ._queued_at ("CE" , 1 )
135
144
await self ._api ._at_command ("WR" )
136
145
137
146
await asyncio .wait_for (self ._api .coordinator_started_event .wait (), timeout = 10 )
138
147
association_state = await asyncio .wait_for (
139
148
self ._get_association_state (), timeout = 10
140
149
)
141
150
LOGGER .debug ("Association state: %s" , association_state )
142
- self ._nwk = await self ._api ._at_command ("MY" )
143
- assert self ._nwk == 0x0000
151
+
152
+ async def force_remove (self , dev ):
153
+ """Forcibly remove device from NCP."""
154
+ pass
144
155
145
156
async def _get_association_state (self ):
146
157
"""Wait for Zigbee to start."""
@@ -266,6 +277,9 @@ async def permit_ncp(self, time_s=60):
266
277
await self ._api ._at_command ("AC" )
267
278
await self ._api ._at_command ("CB" , 2 )
268
279
280
+ async def permit_with_key (self , node , code , time_s = 60 ):
281
+ raise NotImplementedError ("XBee does not support install codes" )
282
+
269
283
def handle_modem_status (self , status ):
270
284
LOGGER .info ("Modem status update: %s (%s)" , status .name , status .value )
271
285
@@ -296,7 +310,7 @@ def handle_rx(
296
310
self .handle_join (nwk , ieee , 0 )
297
311
298
312
try :
299
- self .devices [self .ieee ].last_seen = time .time ()
313
+ self .devices [self .state . node_info . ieee ].last_seen = time .time ()
300
314
except KeyError :
301
315
pass
302
316
try :
@@ -383,7 +397,24 @@ class XBeeGroupResponse(zigpy.quirks.CustomCluster, Groups):
383
397
def __init__ (self , * args , ** kwargs ):
384
398
super ().__init__ (* args , ** kwargs )
385
399
self .node_desc = NodeDescriptor (
386
- 0x00 , 0x40 , 0x8E , 0x101E , 0x52 , 0x00FF , 0x2C00 , 0x00FF , 0x00
400
+ logical_type = NodeDescriptor .LogicalType .Coordinator ,
401
+ complex_descriptor_available = 0 ,
402
+ user_descriptor_available = 0 ,
403
+ reserved = 0 ,
404
+ aps_flags = 0 ,
405
+ frequency_band = NodeDescriptor .FrequencyBand .Freq2400MHz ,
406
+ mac_capability_flags = (
407
+ NodeDescriptor .MACCapabilityFlags .AllocateAddress
408
+ | NodeDescriptor .MACCapabilityFlags .RxOnWhenIdle
409
+ | NodeDescriptor .MACCapabilityFlags .MainsPowered
410
+ | NodeDescriptor .MACCapabilityFlags .FullFunctionDevice
411
+ ),
412
+ manufacturer_code = 4126 ,
413
+ maximum_buffer_size = 82 ,
414
+ maximum_incoming_transfer_size = 255 ,
415
+ server_mask = 11264 ,
416
+ maximum_outgoing_transfer_size = 255 ,
417
+ descriptor_capability_field = NodeDescriptor .DescriptorCapability .NONE ,
387
418
)
388
419
389
420
replacement = {
0 commit comments