1
+ import viam_wrap
2
+ from viam .components .generic import Generic
3
+ from viam .proto .app .robot import ComponentConfig
4
+ from typing import Mapping , Optional , Self
5
+ from viam .utils import ValueTypes
6
+ from viam .proto .common import ResourceName
7
+ from viam .resource .base import ResourceBase
8
+ import sys
9
+
10
+ # Import all board pins and bus interface.
11
+ import board
12
+ import busio
13
+
14
+ # Import the HT16K33 LED matrix module.
15
+ from adafruit_ht16k33 import segments , ht16k33
16
+
17
+ class Ht16k33_Seg14x4 (Generic ):
18
+ MODEL = "michaellee1019:ht16k33:seg_14_x_4"
19
+ i2c = None
20
+ segs = None
21
+
22
+ async def do_command (
23
+ self ,
24
+ command : Mapping [str , ValueTypes ],
25
+ * ,
26
+ timeout : Optional [float ] = None ,
27
+ ** kwargs
28
+ ) -> Mapping [str , ValueTypes ]:
29
+ result = {key : False for key in command .keys ()}
30
+ for (name , args ) in command .items ():
31
+ if name == 'marquee' :
32
+ if 'text' in args :
33
+ #TODO: NoneType is not converted to None
34
+ self .marquee (args ['text' ], args .get ('delay' ))
35
+ result [name ] = True
36
+ else :
37
+ result [name ] = 'missing text parameter'
38
+ if name == 'print' :
39
+ if 'value' in args :
40
+ # TODO: decimal results in Error: TypeError - slice indices must be integers or None or have an __index__ method
41
+ self .print (args ['value' ], args .get ('decimal' ))
42
+ result [name ] = True
43
+ else :
44
+ result [name ] = 'missing value parameter'
45
+ if name == 'print_hex' :
46
+ if 'value' in args :
47
+ self .print_hex (args ['value' ])
48
+ result [name ] = True
49
+ else :
50
+ result [name ] = 'missing value parameter'
51
+ if name == 'scroll' :
52
+ if 'count' in args :
53
+ self .scroll (args ['count' ])
54
+ result [name ] = True
55
+ else :
56
+ result [name ] = 'missing count parameter'
57
+ if name == 'set_digit_raw' :
58
+ if all (k in args for k in ('index' ,'bitmask' )):
59
+ self .set_digit_raw (args ['index' ], args ['bitmask' ])
60
+ result [name ] = True
61
+ else :
62
+ result [name ] = 'missing index and/or bitmask parameters'
63
+ return result
64
+
65
+
66
+ def marquee (self , text : str , delay : float ) -> None :
67
+ # TODO try to support loop
68
+ self .segs .marquee (text , loop = False , delay = 0 if delay is None else delay )
69
+
70
+ def print (self , value , decimal : int ) -> None :
71
+ self .segs .print (value , decimal = 0 if decimal is None else decimal )
72
+
73
+ def print_hex (self , value : int ) -> None :
74
+ self .segs .print_hex (value )
75
+
76
+ def scroll (self , count : int ) -> None :
77
+ # TODO Error: IndexError - bytearray index out of range
78
+ self .segs .scroll (2 )
79
+
80
+ def set_digit_raw (self , index : int , bitmask : int ) -> None :
81
+ # TODO Error: TypeError - unsupported operand type(s) for &=: 'float' and 'int'
82
+ self .segs .set_digit_raw (1 , bitmask )
83
+
84
+ @classmethod
85
+ def new (self , config : ComponentConfig , dependencies : Mapping [ResourceName , ResourceBase ]) -> Self :
86
+ self .i2c = busio .I2C (board .SCL , board .SDA )
87
+
88
+ brightness = None
89
+ auto_write = None
90
+ if 'brightness' in config .attributes .fields :
91
+ brightness = config .attributes .fields ["brightness" ].number_value
92
+ if 'auto_write' in config .attributes .fields :
93
+ auto_write = config .attributes .fields ["auto_write" ].bool_value
94
+
95
+ addresses = config .attributes .fields ["addresses" ].list_value
96
+ hex_addresses = []
97
+ for address in addresses :
98
+ hex_addresses .append (int (address ,16 ))
99
+ # set brightness through base class
100
+
101
+ self .segs = segments .Seg14x4 (
102
+ i2c = self .i2c ,
103
+ address = hex_addresses ,
104
+ auto_write = True if auto_write is None else auto_write ,
105
+ chars_per_display = 4 )
106
+
107
+ if brightness is not None :
108
+ ht16k33 .HT16K33 (self .i2c , hex_addresses , brightness = brightness )
109
+
110
+ output = self (config .name )
111
+ return output
112
+
113
+ @classmethod
114
+ def validate_config (self , config : ComponentConfig ) -> None :
115
+ addresses = config .attributes .fields ["addresses" ].list_value
116
+ if addresses is None :
117
+ raise Exception ('A address attribute is required for seg_14_x_4 component. Must be a string array of 1 or more addresses in hexidecimal format such as "0x00".' )
118
+
119
+ # TODO: assert len()>1, parse addresses here
120
+
121
+ return None
122
+
123
+ if __name__ == '__main__' :
124
+ # necessary for pyinstaller to see it
125
+ # build this with:
126
+ # pyinstaller --onefile --hidden-import viam-wrap --paths $VIRTUAL_ENV/lib/python3.10/site-packages installable.py
127
+ # `--paths` arg may no longer be necessary once viam-wrap is published somewhere
128
+ # todo: utility to append this stanza automatically at build time
129
+ viam_wrap .main (sys .modules .get (__name__ ))
0 commit comments