Skip to content

Commit e79e47b

Browse files
author
James Wah
committed
device/sequences: add Commissioning
1 parent 15bcee3 commit e79e47b

File tree

1 file changed

+125
-1
lines changed

1 file changed

+125
-1
lines changed

dali/device/sequences.py

+125-1
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,37 @@
66
import types
77
from typing import Generator, Optional, Type
88

9-
from dali.address import DeviceShort, InstanceNumber
9+
from dali.address import DeviceShort, InstanceNumber, DeviceBroadcast, DeviceBroadcastUnaddressed
1010
from dali.command import Command, Response
1111
from dali.device.general import (
12+
Compare,
1213
DTR0,
1314
DTR1,
1415
DTR2,
1516
EventScheme,
17+
Initialise,
1618
InstanceEventFilter,
19+
ProgramShortAddress,
20+
QueryDeviceStatus,
1721
QueryEventFilterH,
1822
QueryEventFilterL,
1923
QueryEventFilterM,
2024
QueryEventScheme,
2125
QueryEventSchemeResponse,
26+
Randomise,
27+
SearchAddrH,
28+
SearchAddrL,
29+
SearchAddrM,
2230
SetEventFilter,
2331
SetEventScheme,
32+
SetShortAddress,
33+
Terminate,
34+
VerifyShortAddress,
35+
Withdraw,
2436
)
2537
from dali.device.helpers import check_bad_rsp
38+
from dali.exceptions import ProgramShortAddressFailure
39+
from dali.sequences import progress, sleep
2640

2741

2842
def SetEventSchemes(
@@ -225,3 +239,113 @@ def QueryEventFilters(
225239
hi = rsp.value
226240

227241
return filter_type(int.from_bytes((lo, md, hi), "little"))
242+
243+
def _find_next(low, high):
244+
yield SearchAddrH((high >> 16) & 0xff)
245+
yield SearchAddrM((high >> 8) & 0xff)
246+
yield SearchAddrL(high & 0xff)
247+
248+
r = yield Compare()
249+
250+
if low == high:
251+
if r.value is True:
252+
return "clash" if r.raw_value.error else low
253+
return
254+
255+
if r.value is True:
256+
midpoint = (low + high) // 2
257+
res = yield from _find_next(low, midpoint)
258+
if res is not None:
259+
return res
260+
return (yield from _find_next(midpoint + 1, high))
261+
262+
263+
def Commissioning(available_addresses=None, readdress=False,
264+
dry_run=False):
265+
"""Assign short addresses to control gear
266+
267+
If available_addresses is passed, only the specified addresses
268+
will be assigned; otherwise all short addresses are considered to
269+
be available.
270+
271+
if "readdress" is set, all existing short addresses will be
272+
cleared; otherwise, only control gear that is currently
273+
unaddressed will have short addresses assigned.
274+
275+
If "dry_run" is set then no short addresses will actually be set.
276+
This can be useful for testing.
277+
"""
278+
if available_addresses is None:
279+
available_addresses = list(range(64))
280+
else:
281+
available_addresses = list(available_addresses)
282+
283+
if readdress:
284+
if dry_run:
285+
yield progress(message="dry_run is set: not deleting existing "
286+
"short addresses")
287+
else:
288+
yield DTR0(255)
289+
yield SetShortAddress(DeviceBroadcast())
290+
else:
291+
# We need to know which short addresses are already in use
292+
for a in range(0, 64):
293+
if a in available_addresses:
294+
in_use = yield QueryDeviceStatus(Short(a))
295+
if in_use.value:
296+
available_addresses.remove(a)
297+
yield progress(
298+
message=f"Available addresses: {available_addresses}")
299+
300+
yield Terminate()
301+
yield Initialise(0xff if readdress else 0x7f)
302+
303+
finished = False
304+
# We loop here to cope with multiple devices picking the same
305+
# random search address; when we discover that, we
306+
# re-randomise and begin again. Devices that have already
307+
# received addresses are unaffected.
308+
while not finished:
309+
yield Randomise()
310+
# Randomise can take up to 100ms
311+
yield sleep(0.1)
312+
313+
low = 0
314+
high = 0xffffff
315+
316+
while low is not None:
317+
yield progress(completed=low, size=high)
318+
low = yield from _find_next(low, high)
319+
if low == "clash":
320+
yield progress(message="Multiple ballasts picked the same "
321+
"random address; restarting")
322+
break
323+
if low is None:
324+
finished = True
325+
break
326+
yield progress(
327+
message=f"Ballast found at address {low:#x}")
328+
if available_addresses:
329+
new_addr = available_addresses.pop(0)
330+
if dry_run:
331+
yield progress(
332+
message="Not programming short address "
333+
f"{new_addr} because dry_run is set")
334+
else:
335+
yield progress(
336+
message=f"Programming short address {new_addr}")
337+
yield ProgramShortAddress(new_addr)
338+
r = yield VerifyShortAddress(new_addr)
339+
if r.value is not True:
340+
raise ProgramShortAddressFailure(new_addr)
341+
else:
342+
yield progress(
343+
message="Device found but no short addresses left")
344+
yield Withdraw()
345+
if low < high:
346+
low = low + 1
347+
else:
348+
low = None
349+
finished = True
350+
yield Terminate()
351+
yield progress(message="Addressing complete")

0 commit comments

Comments
 (0)