Skip to content

Commit cc24b6e

Browse files
authored
Merge pull request #231 from mkinney/camelCase
handle snake_case or camelCase
2 parents e0edbc6 + db09b47 commit cc24b6e

File tree

8 files changed

+464
-43
lines changed

8 files changed

+464
-43
lines changed

exampleConfig.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# example config using camelCase keys
2+
owner: Bob TBeam
3+
4+
channelUrl: https://www.meshtastic.org/d/#CgUYAyIBAQ
5+
6+
location:
7+
lat: 35.88888
8+
lon: -93.88888
9+
alt: 304
10+
11+
userPrefs:
12+
region: 1
13+
isAlwaysPowered: 'true'
14+
sendOwnerInterval: 2
15+
screenOnSecs: 31536000
16+
waitBluetoothSecs: 31536000

example_config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# example configuration file with snake_case keys
12
owner: Bob TBeam
23

34
channel_url: https://www.meshtastic.org/d/#CgUYAyIBAQ

meshtastic/__main__.py

Lines changed: 80 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from meshtastic.globals import Globals
2121
from meshtastic.__init__ import BROADCAST_ADDR
2222

23-
2423
def onReceive(packet, interface):
2524
"""Callback invoked when a packet arrives"""
2625
our_globals = Globals.getInstance()
@@ -56,43 +55,63 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=W0613
5655
def getPref(attributes, name):
5756
"""Get a channel or preferences value"""
5857

58+
camel_name = meshtastic.util.snake_to_camel(name)
59+
# Note: protobufs has the keys in snake_case, so snake internally
60+
snake_name = meshtastic.util.camel_to_snake(name)
61+
logging.debug(f'snake_name:{snake_name} camel_name:{camel_name}')
62+
logging.debug(f'use camel:{Globals.getInstance().get_camel_case()}')
63+
5964
objDesc = attributes.DESCRIPTOR
60-
field = objDesc.fields_by_name.get(name)
65+
field = objDesc.fields_by_name.get(snake_name)
6166
if not field:
62-
print(f"{attributes.__class__.__name__} does not have an attribute called {name}, so you can not get it.")
67+
if Globals.getInstance().get_camel_case():
68+
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not get it.")
69+
else:
70+
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not get it.")
6371
print(f"Choices in sorted order are:")
6472
names = []
6573
for f in objDesc.fields:
66-
names.append(f'{f.name}')
74+
tmp_name = f'{f.name}'
75+
if Globals.getInstance().get_camel_case():
76+
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
77+
names.append(tmp_name)
6778
for temp_name in sorted(names):
6879
print(f" {temp_name}")
6980
return
7081

71-
# okay - try to read the value
72-
try:
73-
try:
74-
val = getattr(attributes, name)
75-
except TypeError:
76-
# The getter didn't like our arg type guess try again as a string
77-
val = getattr(attributes, name)
78-
79-
# succeeded!
80-
print(f"{name}: {str(val)}")
81-
except Exception as ex:
82-
print(f"Can't get {name} due to {ex}")
82+
# read the value
83+
val = getattr(attributes, snake_name)
84+
85+
if Globals.getInstance().get_camel_case():
86+
print(f"{camel_name}: {str(val)}")
87+
logging.debug(f"{camel_name}: {str(val)}")
88+
else:
89+
print(f"{snake_name}: {str(val)}")
90+
logging.debug(f"{snake_name}: {str(val)}")
8391

8492

8593
def setPref(attributes, name, valStr):
8694
"""Set a channel or preferences value"""
8795

96+
snake_name = meshtastic.util.camel_to_snake(name)
97+
camel_name = meshtastic.util.snake_to_camel(name)
98+
logging.debug(f'snake_name:{snake_name}')
99+
logging.debug(f'camel_name:{camel_name}')
100+
88101
objDesc = attributes.DESCRIPTOR
89-
field = objDesc.fields_by_name.get(name)
102+
field = objDesc.fields_by_name.get(snake_name)
90103
if not field:
91-
print(f"{attributes.__class__.__name__} does not have an attribute called {name}, so you can not set it.")
104+
if Globals.getInstance().get_camel_case():
105+
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not set it.")
106+
else:
107+
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not set it.")
92108
print(f"Choices in sorted order are:")
93109
names = []
94110
for f in objDesc.fields:
95-
names.append(f'{f.name}')
111+
tmp_name = f'{f.name}'
112+
if Globals.getInstance().get_camel_case():
113+
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
114+
names.append(tmp_name)
96115
for temp_name in sorted(names):
97116
print(f" {temp_name}")
98117
return
@@ -107,27 +126,27 @@ def setPref(attributes, name, valStr):
107126
if e:
108127
val = e.number
109128
else:
110-
print(f"{name} does not have an enum called {val}, so you can not set it.")
129+
if Globals.getInstance().get_camel_case():
130+
print(f"{camel_name} does not have an enum called {val}, so you can not set it.")
131+
else:
132+
print(f"{snake_name} does not have an enum called {val}, so you can not set it.")
111133
print(f"Choices in sorted order are:")
112134
names = []
113135
for f in enumType.values:
114-
names.append(f'{f.name}')
136+
tmp_name = f'{f.name}'
137+
if Globals.getInstance().get_camel_case():
138+
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
139+
names.append(name)
115140
for temp_name in sorted(names):
116141
print(f" {temp_name}")
117142
return
118143

119-
# okay - try to read the value
120-
try:
121-
try:
122-
setattr(attributes, name, val)
123-
except TypeError:
124-
# The setter didn't like our arg type guess try again as a string
125-
setattr(attributes, name, valStr)
126-
127-
# succeeded!
128-
print(f"Set {name} to {valStr}")
129-
except Exception as ex:
130-
print(f"Can't set {name} due to {ex}")
144+
setattr(attributes, snake_name, val)
145+
146+
if Globals.getInstance().get_camel_case():
147+
print(f"Set {camel_name} to {valStr}")
148+
else:
149+
print(f"Set {snake_name} to {valStr}")
131150

132151

133152
def onConnected(interface):
@@ -311,6 +330,10 @@ def onConnected(interface):
311330
print("Setting channel url to", configuration['channel_url'])
312331
interface.getNode(args.dest).setURL(configuration['channel_url'])
313332

333+
if 'channelUrl' in configuration:
334+
print("Setting channel url to", configuration['channelUrl'])
335+
interface.getNode(args.dest).setURL(configuration['channelUrl'])
336+
314337
if 'location' in configuration:
315338
alt = 0
316339
lat = 0.0
@@ -340,6 +363,13 @@ def onConnected(interface):
340363
print("Writing modified preferences to device")
341364
interface.getNode(args.dest).writeConfig()
342365

366+
if 'userPrefs' in configuration:
367+
prefs = interface.getNode(args.dest).radioConfig.preferences
368+
for pref in configuration['userPrefs']:
369+
setPref(prefs, pref, str(configuration['userPrefs'][pref]))
370+
print("Writing modified preferences to device")
371+
interface.getNode(args.dest).writeConfig()
372+
343373
if args.export_config:
344374
# export the configuration (the opposite of '--configure')
345375
closeNow = True
@@ -548,7 +578,10 @@ def export_config(interface):
548578
if owner:
549579
config += f"owner: {owner}\n\n"
550580
if channel_url:
551-
config += f"channel_url: {channel_url}\n\n"
581+
if Globals.getInstance().get_camel_case():
582+
config += f"channelUrl: {channel_url}\n\n"
583+
else:
584+
config += f"channel_url: {channel_url}\n\n"
552585
if lat or lon or alt:
553586
config += "location:\n"
554587
if lat:
@@ -561,9 +594,16 @@ def export_config(interface):
561594
preferences = f'{interface.localNode.radioConfig.preferences}'
562595
prefs = preferences.splitlines()
563596
if prefs:
564-
config += "user_prefs:\n"
597+
if Globals.getInstance().get_camel_case():
598+
config += "userPrefs:\n"
599+
else:
600+
config += "user_prefs:\n"
565601
for pref in prefs:
566-
config += f" {meshtastic.util.quoteBooleans(pref)}\n"
602+
if Globals.getInstance().get_camel_case():
603+
# Note: This may not work if the value has '_'
604+
config += f" {meshtastic.util.snake_to_camel(meshtastic.util.quoteBooleans(pref))}\n"
605+
else:
606+
config += f" {meshtastic.util.quoteBooleans(pref)}\n"
567607
print(config)
568608
return config
569609

@@ -692,10 +732,12 @@ def initParser():
692732
action="store_true")
693733

694734
parser.add_argument(
695-
"--get", help="Get a preferences field. Use an invalid field such as '0' to get a list of all fields.", nargs=1, action='append')
735+
"--get", help=("Get a preferences field. Use an invalid field such as '0' to get a list of all fields."
736+
" Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')"),
737+
nargs=1, action='append')
696738

697739
parser.add_argument(
698-
"--set", help="Set a preferences field", nargs=2, action='append')
740+
"--set", help="Set a preferences field. Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')", nargs=2, action='append')
699741

700742
parser.add_argument(
701743
"--seturl", help="Set a channel URL", action="store")

meshtastic/globals.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def __init__(self):
3030
self.channel_index = None
3131
self.logfile = None
3232
self.tunnelInstance = None
33+
# TODO: to migrate to camel_case for v1.3 change this value to True
34+
self.camel_case = False
3335

3436
def reset(self):
3537
"""Reset all of our globals. If you add a member, add it to this method, too."""
@@ -38,6 +40,8 @@ def reset(self):
3840
self.channel_index = None
3941
self.logfile = None
4042
self.tunnelInstance = None
43+
# TODO: to migrate to camel_case for v1.3 change this value to True
44+
self.camel_case = False
4145

4246
# setters
4347
def set_args(self, args):
@@ -60,6 +64,10 @@ def set_tunnelInstance(self, tunnelInstance):
6064
"""Set the tunnelInstance"""
6165
self.tunnelInstance = tunnelInstance
6266

67+
def set_camel_case(self):
68+
"""Force using camelCase for things like prefs/set/set"""
69+
self.camel_case = True
70+
6371
# getters
6472
def get_args(self):
6573
"""Get args"""
@@ -80,3 +88,7 @@ def get_logfile(self):
8088
def get_tunnelInstance(self):
8189
"""Get tunnelInstance"""
8290
return self.tunnelInstance
91+
92+
def get_camel_case(self):
93+
"""Get whether or not to use camelCase"""
94+
return self.camel_case

meshtastic/node.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
from meshtastic.util import pskToString, stripnl, Timeout, our_exit, fromPSK
99

1010

11-
12-
1311
class Node:
1412
"""A model of a (local or remote) node in the mesh
1513

0 commit comments

Comments
 (0)