@@ -87,13 +87,22 @@ def checkChannel(interface: MeshInterface, channelIndex: int) -> bool:
8787
8888def getPref (node , comp_name ) -> bool :
8989 """Get a channel or preferences value"""
90+ def _printSetting (config_type , uni_name , pref_value , repeated ):
91+ """Pretty print the setting"""
92+ if repeated :
93+ pref_value = [meshtastic .util .toStr (v ) for v in pref_value ]
94+ else :
95+ pref_value = meshtastic .util .toStr (pref_value )
96+ print (f"{ str (config_type .name )} .{ uni_name } : { str (pref_value )} " )
97+ logging .debug (f"{ str (config_type .name )} .{ uni_name } : { str (pref_value )} " )
9098
9199 name = splitCompoundName (comp_name )
92100 wholeField = name [0 ] == name [1 ] # We want the whole field
93101
94102 camel_name = meshtastic .util .snake_to_camel (name [1 ])
95103 # Note: protobufs has the keys in snake_case, so snake internally
96104 snake_name = meshtastic .util .camel_to_snake (name [1 ])
105+ uni_name = camel_name if mt_config .camel_case else snake_name
97106 logging .debug (f"snake_name:{ snake_name } camel_name:{ camel_name } " )
98107 logging .debug (f"use camel:{ mt_config .camel_case } " )
99108
@@ -112,14 +121,9 @@ def getPref(node, comp_name) -> bool:
112121 break
113122
114123 if not found :
115- if mt_config .camel_case :
116- print (
117- f"{ localConfig .__class__ .__name__ } and { moduleConfig .__class__ .__name__ } do not have an attribute { snake_name } ."
118- )
119- else :
120- print (
121- f"{ localConfig .__class__ .__name__ } and { moduleConfig .__class__ .__name__ } do not have attribute { snake_name } ."
122- )
124+ print (
125+ f"{ localConfig .__class__ .__name__ } and { moduleConfig .__class__ .__name__ } do not have attribute { uni_name } ."
126+ )
123127 print ("Choices are..." )
124128 printConfig (localConfig )
125129 printConfig (moduleConfig )
@@ -131,19 +135,12 @@ def getPref(node, comp_name) -> bool:
131135 config_values = getattr (config , config_type .name )
132136 if not wholeField :
133137 pref_value = getattr (config_values , pref .name )
134- if mt_config .camel_case :
135- print (f"{ str (config_type .name )} .{ camel_name } : { str (pref_value )} " )
136- logging .debug (
137- f"{ str (config_type .name )} .{ camel_name } : { str (pref_value )} "
138- )
139- else :
140- print (f"{ str (config_type .name )} .{ snake_name } : { str (pref_value )} " )
141- logging .debug (
142- f"{ str (config_type .name )} .{ snake_name } : { str (pref_value )} "
143- )
138+ repeated = pref .label == pref .LABEL_REPEATED
139+ _printSetting (config_type , uni_name , pref_value , repeated )
144140 else :
145- print (f"{ str (config_type .name )} :\n { str (config_values )} " )
146- logging .debug (f"{ str (config_type .name )} : { str (config_values )} " )
141+ for field in config_values .ListFields ():
142+ repeated = field [0 ].label == field [0 ].LABEL_REPEATED
143+ _printSetting (config_type , field [0 ].name , field [1 ], repeated )
147144 else :
148145 # Always show whole field for remote node
149146 node .requestConfig (config_type )
@@ -168,18 +165,19 @@ def traverseConfig(config_root, config, interface_config) -> bool:
168165 if isinstance (config [pref ], dict ):
169166 traverseConfig (pref_name , config [pref ], interface_config )
170167 else :
171- setPref (interface_config , pref_name , str ( config [pref ]) )
168+ setPref (interface_config , pref_name , config [pref ])
172169
173170 return True
174171
175172
176- def setPref (config , comp_name , valStr ) -> bool :
173+ def setPref (config , comp_name , raw_val ) -> bool :
177174 """Set a channel or preferences value"""
178175
179176 name = splitCompoundName (comp_name )
180177
181178 snake_name = meshtastic .util .camel_to_snake (name [- 1 ])
182179 camel_name = meshtastic .util .snake_to_camel (name [- 1 ])
180+ uni_name = camel_name if mt_config .camel_case else snake_name
183181 logging .debug (f"snake_name:{ snake_name } " )
184182 logging .debug (f"camel_name:{ camel_name } " )
185183
@@ -201,10 +199,13 @@ def setPref(config, comp_name, valStr) -> bool:
201199 if (not pref ) or (not config_type ):
202200 return False
203201
204- val = meshtastic .util .fromStr (valStr )
205- logging .debug (f"valStr:{ valStr } val:{ val } " )
202+ if isinstance (raw_val , str ):
203+ val = meshtastic .util .fromStr (raw_val )
204+ else :
205+ val = raw_val
206+ logging .debug (f"valStr:{ raw_val } val:{ val } " )
206207
207- if snake_name == "wifi_psk" and len (valStr ) < 8 :
208+ if snake_name == "wifi_psk" and len (str ( raw_val ) ) < 8 :
208209 print (f"Warning: network.wifi_psk must be 8 or more characters." )
209210 return False
210211
@@ -216,14 +217,9 @@ def setPref(config, comp_name, valStr) -> bool:
216217 if e :
217218 val = e .number
218219 else :
219- if mt_config .camel_case :
220- print (
221- f"{ name [0 ]} .{ camel_name } does not have an enum called { val } , so you can not set it."
222- )
223- else :
224- print (
225- f"{ name [0 ]} .{ snake_name } does not have an enum called { val } , so you can not set it."
226- )
220+ print (
221+ f"{ name [0 ]} .{ uni_name } does not have an enum called { val } , so you can not set it."
222+ )
227223 print (f"Choices in sorted order are:" )
228224 names = []
229225 for f in enumType .values :
@@ -244,24 +240,26 @@ def setPref(config, comp_name, valStr) -> bool:
244240 except TypeError :
245241 # The setter didn't like our arg type guess try again as a string
246242 config_values = getattr (config_part , config_type .name )
247- setattr (config_values , pref .name , valStr )
243+ setattr (config_values , pref .name , str (val ))
244+ elif type (val ) == list :
245+ new_vals = [meshtastic .util .fromStr (x ) for x in val ]
246+ config_values = getattr (config , config_type .name )
247+ getattr (config_values , pref .name )[:] = new_vals
248248 else :
249249 config_values = getattr (config , config_type .name )
250250 if val == 0 :
251251 # clear values
252252 print (f"Clearing { pref .name } list" )
253253 del getattr (config_values , pref .name )[:]
254254 else :
255- print (f"Adding '{ val } ' to the { pref .name } list" )
255+ print (f"Adding '{ raw_val } ' to the { pref .name } list" )
256256 cur_vals = [x for x in getattr (config_values , pref .name ) if x not in [0 , "" , b"" ]]
257257 cur_vals .append (val )
258258 getattr (config_values , pref .name )[:] = cur_vals
259+ return True
259260
260261 prefix = f"{ '.' .join (name [0 :- 1 ])} ." if config_type .message_type is not None else ""
261- if mt_config .camel_case :
262- print (f"Set { prefix } { camel_name } to { valStr } " )
263- else :
264- print (f"Set { prefix } { snake_name } to { valStr } " )
262+ print (f"Set { prefix } { uni_name } to { raw_val } " )
265263
266264 return True
267265
@@ -475,13 +473,22 @@ def onConnected(interface):
475473 else :
476474 channelIndex = mt_config .channel_index or 0
477475 if checkChannel (interface , channelIndex ):
476+ telemMap = {
477+ "device" : "device_metrics" ,
478+ "environment" : "environment_metrics" ,
479+ "air_quality" : "air_quality_metrics" ,
480+ "airquality" : "air_quality_metrics" ,
481+ "power" : "power_metrics" ,
482+ }
483+ telemType = telemMap .get (args .request_telemetry , "device_metrics" )
478484 print (
479- f"Sending telemetry request to { args .dest } on channelIndex:{ channelIndex } (this could take a while)"
485+ f"Sending { telemType } telemetry request to { args .dest } on channelIndex:{ channelIndex } (this could take a while)"
480486 )
481487 interface .sendTelemetry (
482488 destinationId = args .dest ,
483489 wantResponse = True ,
484490 channelIndex = channelIndex ,
491+ telemetryType = telemType ,
485492 )
486493
487494 if args .request_position :
@@ -622,19 +629,15 @@ def onConnected(interface):
622629
623630 if "alt" in configuration ["location" ]:
624631 alt = int (configuration ["location" ]["alt" ] or 0 )
625- localConfig .position .fixed_position = True
626632 print (f"Fixing altitude at { alt } meters" )
627633 if "lat" in configuration ["location" ]:
628634 lat = float (configuration ["location" ]["lat" ] or 0 )
629- localConfig .position .fixed_position = True
630635 print (f"Fixing latitude at { lat } degrees" )
631636 if "lon" in configuration ["location" ]:
632637 lon = float (configuration ["location" ]["lon" ] or 0 )
633- localConfig .position .fixed_position = True
634638 print (f"Fixing longitude at { lon } degrees" )
635639 print ("Setting device position" )
636- interface .sendPosition (lat , lon , alt )
637- interface .localNode .writeConfig ("position" )
640+ interface .localNode .setFixedPosition (lat , lon , alt )
638641
639642 if "config" in configuration :
640643 localConfig = interface .getNode (args .dest , ** getNode_kwargs ).localConfig
@@ -1029,6 +1032,15 @@ def export_config(interface) -> str:
10291032 prefs [meshtastic .util .snake_to_camel (pref )] = config [pref ]
10301033 else :
10311034 prefs [pref ] = config [pref ]
1035+ # mark base64 encoded fields as such
1036+ if pref == "security" :
1037+ if 'privateKey' in prefs [pref ]:
1038+ prefs [pref ]['privateKey' ] = 'base64:' + prefs [pref ]['privateKey' ]
1039+ if 'publicKey' in prefs [pref ]:
1040+ prefs [pref ]['publicKey' ] = 'base64:' + prefs [pref ]['publicKey' ]
1041+ if 'adminKey' in prefs [pref ]:
1042+ for i in range (len (prefs [pref ]['adminKey' ])):
1043+ prefs [pref ]['adminKey' ][i ] = 'base64:' + prefs [pref ]['adminKey' ][i ]
10321044 if mt_config .camel_case :
10331045 configObj ["config" ] = config #Identical command here and 2 lines below?
10341046 else :
@@ -1593,10 +1605,14 @@ def addRemoteActionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPar
15931605
15941606 group .add_argument (
15951607 "--request-telemetry" ,
1596- help = "Request telemetry from a node. "
1608+ help = "Request telemetry from a node. With an argument, requests that specific type of telemetry. "
15971609 "You need to pass the destination ID as argument with '--dest'. "
15981610 "For repeaters, the nodeNum is required." ,
1599- action = "store_true" ,
1611+ action = "store" ,
1612+ nargs = "?" ,
1613+ default = None ,
1614+ const = "device" ,
1615+ metavar = "TYPE" ,
16001616 )
16011617
16021618 group .add_argument (
@@ -1843,7 +1859,7 @@ def initParser():
18431859
18441860 power_group .add_argument (
18451861 "--slog" ,
1846- help = "Store structured-logs (slogs) for this run, optionally you can specifiy a destination directory" ,
1862+ help = "Store structured-logs (slogs) for this run, optionally you can specify a destination directory" ,
18471863 nargs = "?" ,
18481864 default = None ,
18491865 const = "default" ,
0 commit comments