Skip to content

Commit 5935a72

Browse files
authored
Merge pull request #16 from SvenHaedrich/add-tests-for-hid-interface
Add tests for hid interface
2 parents 47e61c4 + 41ecd90 commit 5935a72

File tree

16 files changed

+714
-415
lines changed

16 files changed

+714
-415
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ env
22
*.pyc
33
__pycache__
44
.vscode
5-
.coverage
5+
.coverage
6+
local.todo

Addressing_103.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## Representation of Event Scheme
2+
3+
See also IEC 62386-103:2022 9.7.3
4+
| eventScheme | Description | Representation |
5+
|-------------|----------------------------------------------------------------------------------|----------------|
6+
| 0 (default) | Instance addressing, using instance type *t* and number *i*. | T*t*, I*i* |
7+
| 1 | Device addressing, using short address *s* and instance type *t*. | A*s*, T*t* |
8+
| 2 | Device and instance addressing, using short address *s* and instance number *i*. | A*s*, I*i* |
9+
| 3 | Device group addressing, using device group *g* and instance type *t*. | G*g*, T*t* |
10+
| 4 | Instance group addressing, using instance group *n* and type *t*. | IG*n*, T*t* |
11+
12+
## Instance Types
13+
14+
See also IEC 62386-103:2022 Table 4
15+
| instance type | IEC 62386 | Description |
16+
|---------------|-----------|-------------------------------------------------------|
17+
| 0 | 103 | Generic devices that do not implement a specific type |
18+
| 1 | 301 | Input devices - Push buttons |
19+
| 2 | 302 | Absolute input devices |
20+
| 3 | 303 | Occupancy sensors |
21+
| 4 | 304 | Light sensors |
22+
23+
## Representation of Instance Addressing Modes
24+
25+
See also IEC 62386-103:2022 Table 2
26+
| Addressing | Representation |
27+
|-------------------------------------|----------------|
28+
| Instance Number | I*n* |
29+
| Instance Group | IG*g* |
30+
| Instance Type | T*t* |
31+
| Feature on instance number level | FI*n* |
32+
| Feature on instance group level | FG*g* |
33+
| Feature on instance type level | FT*t* |
34+
| Feature broadcast | F BC |
35+
| Feature on instance broadcast level | F INST BC |
36+
| Instance broadcast | INST BC |
37+
| Feature on device level | F DEV |

README.md

Lines changed: 65 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,35 @@ This script converts DALI codes into human readable messages. DALI is the digita
66

77
The source for the DALI code aka frames can be one of
88
* stdin
9-
* Lunatone DALI / USB converter (see: [Lunatone](https://www.lunatone.com/produkt/dali-usb/))
9+
* HID class DALI / USB converter (e.g. [Lunatone](https://www.lunatone.com/produkt/dali-usb/))
1010

1111
This script is based on the following standards
1212
* IEC 62386-101 system components
1313
* IEC 62386-102 control gear
14-
* IEC 62386-103 control device
14+
* IEC 62386-103:2022 control device
1515
* IEC 62386-207 LED module DT6
1616
* IEC 62386-208 switching function DT7
1717
* IEC 62386-209 colour control DT8
1818

1919
## Run
2020

21-
```bash
22-
dali_mon [options]
23-
```
21+
dali_mon [options]
22+
23+
## Stop
24+
25+
Use `Ctrl-C` to stop a running instance of `dali_mon`.
2426

2527
## Sample Output
2628

27-
<pre> ./dali_mon < /dev/ttyUSB0
28-
11618.586 | 0.000 | FF00 | BC OFF
29-
11619.459 | 0.873 | FF06 | BC RECALL MIN LEVEL
30-
11620.596 | 1.137 | FF05 | BC RECALL MAX LEVEL
31-
11626.347 | 5.751 | FF02 | BC DOWN
32-
11626.880 | 0.533 | FF02 | BC DOWN
33-
11627.332 | 0.452 | FF02 | BC DOWN
34-
11633.766 | 6.434 | FF11 | BC GO TO SCENE 1
35-
11635.703 | 1.937 | FF00 | BC OFF
36-
</pre>
37-
38-
## Representation of Event Scheme
39-
40-
See also IEC 62386-103:2022 9.7.3
41-
| eventScheme | Description | Representation |
42-
|-------------|--------------------------------------------------------------------------|----------------|
43-
| 0 (default) | Instance addressing, using instance type *t* and number *i*. | T*t*, I*i* |
44-
| 1 | Device addressing, using short address *s* and instance type *t*. | A*s*, T*t* |
45-
| 2 | Device and instance addressing, using short address *s* and instance number *i*. | A*s*, I*i* |
46-
| 3 | Device group addressing, using device group *g* and instance type *t*. | G*g*, T*t* |
47-
| 4 | Instance group addressing, using instance group *n* and type *t*. | IG*n*, T*t* |
29+
./dali_mon < /dev/ttyUSB0
30+
11618.586 | 0.000 | FF00 | BC OFF
31+
11619.459 | 0.873 | FF06 | BC RECALL MIN LEVEL
32+
11620.596 | 1.137 | FF05 | BC RECALL MAX LEVEL
33+
11626.347 | 5.751 | FF02 | BC DOWN
34+
11626.880 | 0.533 | FF02 | BC DOWN
35+
11627.332 | 0.452 | FF02 | BC DOWN
36+
11633.766 | 6.434 | FF11 | BC GO TO SCENE 1
37+
11635.703 | 1.937 | FF00 | BC OFF
4838

4939
## Read from Serial Port
5040

@@ -56,77 +46,76 @@ stty -F /dev/ttyUSB0 115200 litout -crtscts
5646

5747
## Commandline Parameters
5848

59-
| Option | Short | Usage |
60-
|-----------|-------|-------------------------------------------------------------|
61-
|--help | | Show help message and exit. |
62-
|--version | | Show the version information and exit. |
63-
|--nocolor | | Do not use color coding for output. |
64-
|--absolute | | Add absolute time from host machine to output. |
65-
|--echo | | Echo unprocessed input line to output. |
66-
|--lunatone | -l | Use Lunatone USB connector for DALI communication. |
67-
|--debug | | Enable debug level logging. |
49+
| Option | Short | Usage |
50+
|-----------|-------|-----------------------------------------------------|
51+
|--help | | Show help message and exit. |
52+
|--version | | Show the version information and exit. |
53+
|--nocolor | | Do not use color coding for output. |
54+
|--absolute | | Add absolute time from host machine to output. |
55+
|--echo | | Echo unprocessed input line to output. |
56+
|--hid | -l | Use HID class USB connector for DALI communication. |
57+
|--debug | | Enable debug level logging. |
6858

6959
### Output Columns
7060

7161
* if enabled: absolute timestamp (from host machine)
72-
* timestamp in seconds (from serial message, or host machine for lunatone interface)
62+
* timestamp in seconds (from serial message, or host machine for USB HID interface)
7363
* delta time to previous command
7464
* hex data received
7565
* DALI command translation
7666

7767
## Install
78-
```
79-
git clone git@github.com:SvenHaedrich/dali_mon.git
80-
cd dali_mon
81-
python -m venv env
82-
```
68+
69+
git clone git@github.com:SvenHaedrich/dali_mon.git
70+
cd dali_mon
71+
python -m venv env
72+
8373
For the Lunatone USB adapter you need to copy the file `99-lunatone-dali.rules` into the `udev` folder
8474
and reload the `udev` rules.
8575

86-
```
87-
sudo cp 99-lunatone-dali.rules /etc/udev/rules.d/
88-
sudo udevadm control --reload-rules
89-
```
76+
sudo cp 99-lunatone-dali.rules /etc/udev/rules.d/
77+
sudo udevadm control --reload-rules
78+
9079
This file grants everyone read/write access. If you want to restrict access,
91-
you should modify MODE to "0660". You can then grant access to specific user
80+
you should modify `MODE` to `0660`. You can then grant access to specific user
9281
accounts by adding them to the plugdev group. Note that some Linux dirstibutions always require a per user permission. To grant permission to a specific user:
93-
```
94-
sudo usermod -a -G plugdev username
95-
```
82+
83+
sudo usermod -a -G plugdev username
84+
9685
You will have to log out and then back in for the group change to take effect.
9786

87+
## Address- and Event-Schemes for Control Devices
88+
89+
see [Addressing](Adressing_103.md)
90+
9891
## Tests
9992

100-
To run the tests enter
101-
```
102-
cd tests
103-
./run_tests.sh
104-
```
93+
see [README.md](tests/README.md)
10594

10695
## DALI frame format for serial input
10796

10897
Each DALI frame is expected to use the following format:
109-
```
110-
"{" <timestamp> <error> <bits> " " <data> "}"
111-
```
98+
99+
"{" <timestamp> <error> <bits> " " <data> "}"
100+
112101
Only information framed by curly braces is interpreted. <br/>
113-
```
114-
<timestamp> : integer number,
115-
each tick represents 1 millisecond,
116-
number is given in hex presentation,
117-
fixed length of 8 digits
118-
<error> : either a
119-
"-" (minus) indicating normal state, or
120-
"*" (asteriks) inidcating an error
121-
<bits> : data bits received,
122-
number is given in hex presentation,
123-
fixed length of 2 digits
124-
<data> : received data payload,
125-
number is given in hex presentation,
126-
fixed length of 8 digits
127-
```
102+
103+
<timestamp> : integer number,
104+
each tick represents 1 millisecond,
105+
number is given in hex presentation,
106+
fixed length of 8 digits
107+
<error> : either a
108+
"-" (minus) indicating normal state, or
109+
"*" (asteriks) inidcating an error
110+
<bits> : data bits received,
111+
number is given in hex presentation,
112+
fixed length of 2 digits
113+
<data> : received data payload,
114+
number is given in hex presentation,
115+
fixed length of 8 digits
116+
128117
In case of an error state:<br/>
129-
```
130-
<bits> : codes the error code
131-
<data> : contains additional error information
132-
```
118+
119+
<bits> : codes the error code
120+
<data> : contains additional error information
121+

source/DALI/raw_frame.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
class Raw_Frame:
7-
COMMAND = "-"
7+
VALID = "-"
88
ERROR = "*"
99
INVALID = " "
1010

@@ -13,6 +13,7 @@ def reset_self(self):
1313
self.type = self.INVALID
1414
self.length = 0
1515
self.data = 0
16+
self.send_twice = False
1617

1718
def from_line(self, line):
1819
if self.echo:
@@ -30,6 +31,19 @@ def from_line(self, line):
3031
self.type = self.INVALID
3132
logger.debug(f"Raw frame, length {self.length} data 0x{self.data:08X}")
3233

33-
def __init__(self, echo=False):
34+
def __eq__(self, other):
35+
if not isinstance(other, Raw_Frame):
36+
return NotImplemented
37+
return self.length == other.length and self.data == other.data
38+
39+
def __str__(self):
40+
return f"DALI frame <{self.timestamp:.03f}{self.type}{self.length:02X} 0x{self.data:X}>"
41+
42+
def __init__(self, length=0, data=0, send_twice=False, echo=False):
3443
self.echo = echo
3544
self.reset_self()
45+
if length in range(0x21):
46+
self.type = self.VALID
47+
self.length = length
48+
self.data = data
49+
self.send_twice = send_twice

source/dali_mon.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from termcolor import cprint
66

77
import DALI
8-
import dali_usb
8+
import usb_hid
99

1010
logger = logging.getLogger(__name__)
1111

@@ -24,9 +24,7 @@ def print_local_time(enabled):
2424

2525
def print_command_color(absolute_time, timestamp, delta, dali_command):
2626
print_local_time_color(absolute_time)
27-
cprint(
28-
f"{timestamp:.03f} | {delta:8.03f} | {dali_command} | ", color="green", end=""
29-
)
27+
cprint(f"{timestamp:.03f} | {delta:8.03f} | {dali_command} | ", color="green", end="")
3028
cprint(f"{dali_command.cmd()}", color="white")
3129

3230

@@ -43,9 +41,7 @@ def print_error_color(absolute_time, raw, delta):
4341

4442
def print_error(absolute_time, raw, delta):
4543
print_local_time(absolute_time)
46-
print(
47-
f"{raw.timestamp:.03f} | {delta:8.03f} | {DALI.DALIError(raw.length, raw.data)}"
48-
)
44+
print(f"{raw.timestamp:.03f} | {delta:8.03f} | {DALI.DALIError(raw.length, raw.data)}")
4945

5046

5147
def process_line(raw, no_color, absolute_time):
@@ -54,7 +50,7 @@ def process_line(raw, no_color, absolute_time):
5450
delta = raw.timestamp - process_line.last_timestamp
5551
else:
5652
delta = 0
57-
if raw.type == raw.COMMAND:
53+
if raw.type == raw.VALID:
5854
dali_command = DALI.Decode(raw, process_line.active_device_type)
5955
if no_color:
6056
print_command(absolute_time, raw.timestamp, delta, dali_command)
@@ -71,7 +67,7 @@ def process_line(raw, no_color, absolute_time):
7167

7268
def main_usb(no_color, absolute_time):
7369
logger.debug("read from Lunatone usb device")
74-
dali_connection = dali_usb.DALI_Usb()
70+
dali_connection = usb_hid.DALI_Usb()
7571
dali_connection.start_read()
7672
try:
7773
while True:
@@ -107,15 +103,15 @@ def main_file(transparent, no_color, absolute_time):
107103
@click.version_option("1.1.2")
108104
@click.option(
109105
"-l",
110-
"--lunatone",
111-
help="Use Lunatone USB connector for DALI communication.",
106+
"--hid",
107+
help="Use USB HID class connector for DALI communication.",
112108
is_flag=True,
113109
)
114110
@click.option("--debug", help="Enable debug level logging.", is_flag=True)
115111
@click.option("--nocolor", help="Do not use color coding for output.", is_flag=True)
116112
@click.option("--echo", help="Echo unprocessed input line to output.", is_flag=True)
117113
@click.option("--absolute", help="Add absolute local time to output.", is_flag=True)
118-
def dali_mon(lunatone, debug, nocolor, echo, absolute):
114+
def dali_mon(hid, debug, nocolor, echo, absolute):
119115
"""
120116
Monitor for DALI commands,
121117
SevenLabs 2023
@@ -126,7 +122,7 @@ def dali_mon(lunatone, debug, nocolor, echo, absolute):
126122
process_line.last_timestamp = 0
127123
process_line.active_device_type = DALI.DeviceType.NONE
128124
try:
129-
if lunatone:
125+
if hid:
130126
main_usb(nocolor, absolute)
131127
elif sys.stdin.isatty():
132128
main_tty(echo, nocolor, absolute)

0 commit comments

Comments
 (0)