Skip to content

Commit 30a7162

Browse files
author
James Wah
committed
device/sequences: add Commissioning
1 parent a6ba48f commit 30a7162

File tree

1 file changed

+128
-5
lines changed

1 file changed

+128
-5
lines changed

dali/device/sequences.py

+128-5
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,40 @@
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,
17-
QueryResolution,
18-
QueryInputValue,
19-
QueryInputValueLatch,
19+
ProgramShortAddress,
20+
QueryDeviceStatus,
2021
QueryEventFilterH,
2122
QueryEventFilterL,
2223
QueryEventFilterM,
2324
QueryEventScheme,
2425
QueryEventSchemeResponse,
26+
QueryInputValue,
27+
QueryInputValueLatch,
28+
QueryResolution,
29+
Randomise,
30+
SearchAddrH,
31+
SearchAddrL,
32+
SearchAddrM,
2533
SetEventFilter,
2634
SetEventScheme,
35+
SetShortAddress,
36+
Terminate,
37+
VerifyShortAddress,
38+
Withdraw,
2739
)
2840
from dali.device.helpers import check_bad_rsp
29-
from dali.exceptions import DALISequenceError
41+
from dali.exceptions import DALISequenceError, ProgramShortAddressFailure
42+
from dali.sequences import progress, sleep
3043

3144

3245
def SetEventSchemes(
@@ -275,3 +288,113 @@ def query_input_value(
275288
value >>= 8 - resolution
276289

277290
return value
291+
292+
def _find_next(low, high):
293+
yield SearchAddrH((high >> 16) & 0xff)
294+
yield SearchAddrM((high >> 8) & 0xff)
295+
yield SearchAddrL(high & 0xff)
296+
297+
r = yield Compare()
298+
299+
if low == high:
300+
if r.value is True:
301+
return "clash" if r.raw_value.error else low
302+
return
303+
304+
if r.value is True:
305+
midpoint = (low + high) // 2
306+
res = yield from _find_next(low, midpoint)
307+
if res is not None:
308+
return res
309+
return (yield from _find_next(midpoint + 1, high))
310+
311+
312+
def Commissioning(available_addresses=None, readdress=False,
313+
dry_run=False):
314+
"""Assign short addresses to control gear
315+
316+
If available_addresses is passed, only the specified addresses
317+
will be assigned; otherwise all short addresses are considered to
318+
be available.
319+
320+
if "readdress" is set, all existing short addresses will be
321+
cleared; otherwise, only control gear that is currently
322+
unaddressed will have short addresses assigned.
323+
324+
If "dry_run" is set then no short addresses will actually be set.
325+
This can be useful for testing.
326+
"""
327+
if available_addresses is None:
328+
available_addresses = list(range(64))
329+
else:
330+
available_addresses = list(available_addresses)
331+
332+
if readdress:
333+
if dry_run:
334+
yield progress(message="dry_run is set: not deleting existing "
335+
"short addresses")
336+
else:
337+
yield DTR0(255)
338+
yield SetShortAddress(DeviceBroadcast())
339+
else:
340+
# We need to know which short addresses are already in use
341+
for a in range(0, 64):
342+
if a in available_addresses:
343+
in_use = yield QueryDeviceStatus(Short(a))
344+
if in_use.value:
345+
available_addresses.remove(a)
346+
yield progress(
347+
message=f"Available addresses: {available_addresses}")
348+
349+
yield Terminate()
350+
yield Initialise(0xff if readdress else 0x7f)
351+
352+
finished = False
353+
# We loop here to cope with multiple devices picking the same
354+
# random search address; when we discover that, we
355+
# re-randomise and begin again. Devices that have already
356+
# received addresses are unaffected.
357+
while not finished:
358+
yield Randomise()
359+
# Randomise can take up to 100ms
360+
yield sleep(0.1)
361+
362+
low = 0
363+
high = 0xffffff
364+
365+
while low is not None:
366+
yield progress(completed=low, size=high)
367+
low = yield from _find_next(low, high)
368+
if low == "clash":
369+
yield progress(message="Multiple ballasts picked the same "
370+
"random address; restarting")
371+
break
372+
if low is None:
373+
finished = True
374+
break
375+
yield progress(
376+
message=f"Ballast found at address {low:#x}")
377+
if available_addresses:
378+
new_addr = available_addresses.pop(0)
379+
if dry_run:
380+
yield progress(
381+
message="Not programming short address "
382+
f"{new_addr} because dry_run is set")
383+
else:
384+
yield progress(
385+
message=f"Programming short address {new_addr}")
386+
yield ProgramShortAddress(new_addr)
387+
r = yield VerifyShortAddress(new_addr)
388+
if r.value is not True:
389+
raise ProgramShortAddressFailure(new_addr)
390+
else:
391+
yield progress(
392+
message="Device found but no short addresses left")
393+
yield Withdraw()
394+
if low < high:
395+
low = low + 1
396+
else:
397+
low = None
398+
finished = True
399+
yield Terminate()
400+
yield progress(message="Addressing complete")

0 commit comments

Comments
 (0)