Skip to content

Commit bbc526d

Browse files
authored
Merge pull request #584 from ianmcorvidae/improve-fixed-position
Use new fixed position admin messages and add `--remove-position` argument
2 parents abe98f5 + 1746ad1 commit bbc526d

File tree

3 files changed

+87
-52
lines changed

3 files changed

+87
-52
lines changed

meshtastic/__main__.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -257,33 +257,41 @@ def onConnected(interface):
257257
if not args.export_config:
258258
print("Connected to radio")
259259

260-
if args.setlat or args.setlon or args.setalt:
260+
if args.remove_position:
261+
if args.dest != BROADCAST_ADDR:
262+
print("Setting positions of remote nodes is not supported.")
263+
return
264+
closeNow = True
265+
print("Removing fixed position and disabling fixed position setting")
266+
interface.localNode.removeFixedPosition()
267+
elif args.setlat or args.setlon or args.setalt:
261268
if args.dest != BROADCAST_ADDR:
262269
print("Setting latitude, longitude, and altitude of remote nodes is not supported.")
263270
return
264271
closeNow = True
265272

266273
alt = 0
267-
lat = 0.0
268-
lon = 0.0
269-
localConfig = interface.localNode.localConfig
274+
lat = 0
275+
lon = 0
270276
if args.setalt:
271277
alt = int(args.setalt)
272-
localConfig.position.fixed_position = True
273278
print(f"Fixing altitude at {alt} meters")
274279
if args.setlat:
275-
lat = float(args.setlat)
276-
localConfig.position.fixed_position = True
280+
try:
281+
lat = int(args.setlat)
282+
except ValueError:
283+
lat = float(args.setlat)
277284
print(f"Fixing latitude at {lat} degrees")
278285
if args.setlon:
279-
lon = float(args.setlon)
280-
localConfig.position.fixed_position = True
286+
try:
287+
lon = int(args.setlon)
288+
except ValueError:
289+
lon = float(args.setlon)
281290
print(f"Fixing longitude at {lon} degrees")
282291

283-
print("Setting device position")
292+
print("Setting device position and enabling fixed position setting")
284293
# can include lat/long/alt etc: latitude = 37.5, longitude = -122.1
285-
interface.sendPosition(lat, lon, alt)
286-
interface.localNode.writeConfig("position")
294+
interface.localNode.setFixedPosition(lat, lon, alt)
287295
elif not args.no_time:
288296
# We normally provide a current time to the mesh when we connect
289297
if interface.localNode.nodeNum in interface.nodesByNum and "position" in interface.nodesByNum[interface.localNode.nodeNum]:
@@ -1445,12 +1453,25 @@ def initParser():
14451453
action="store_true",
14461454
)
14471455

1448-
group.add_argument("--setalt", help="Set device altitude in meters (allows use without GPS)")
1456+
group.add_argument(
1457+
"--setalt",
1458+
help="Set device altitude in meters (allows use without GPS), and enable fixed position.",
1459+
)
14491460

1450-
group.add_argument("--setlat", help="Set device latitude (allows use without GPS)")
1461+
group.add_argument(
1462+
"--setlat",
1463+
help="Set device latitude (allows use without GPS), and enable fixed position. Accepts a decimal value or an integer premultiplied by 1e7.",
1464+
)
1465+
1466+
group.add_argument(
1467+
"--setlon",
1468+
help="Set device longitude (allows use without GPS), and enable fixed position. Accepts a decimal value or an integer premultiplied by 1e7.",
1469+
)
14511470

14521471
group.add_argument(
1453-
"--setlon", help="Set device longitude (allows use without GPS)"
1472+
"--remove-position",
1473+
help="Clear any existing fixed position and disable fixed position.",
1474+
action="store_true",
14541475
)
14551476

14561477
group.add_argument(

meshtastic/node.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from typing import Union
99

10-
from meshtastic import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, portnums_pb2
10+
from meshtastic import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2
1111
from meshtastic.util import (
1212
Timeout,
1313
camel_to_snake,
@@ -655,6 +655,38 @@ def resetNodeDb(self):
655655
onResponse = self.onAckNak
656656
return self._sendAdmin(p, onResponse=onResponse)
657657

658+
def setFixedPosition(self, lat: Union[int, float], lon: Union[int, float], alt: int):
659+
"""Tell the node to set fixed position to the provided value and enable the fixed position setting"""
660+
if self != self.iface.localNode:
661+
logging.error("Setting position of remote nodes is not supported.")
662+
return None
663+
664+
p = mesh_pb2.Position()
665+
if isinstance(lat, float) and lat != 0.0:
666+
p.latitude_i = int(lat / 1e-7)
667+
elif isinstance(lat, int) and lat != 0:
668+
p.latitude_i = lat
669+
670+
if isinstance(lon, float) and lon != 0.0:
671+
p.longitude_i = int(lon / 1e-7)
672+
elif isinstance(lon, int) and lon != 0:
673+
p.longitude_i = lon
674+
675+
if alt != 0:
676+
p.altitude = alt
677+
678+
a = admin_pb2.AdminMessage()
679+
a.set_fixed_position.CopyFrom(p)
680+
return self._sendAdmin(a)
681+
682+
def removeFixedPosition(self):
683+
"""Tell the node to remove the fixed position and set the fixed position setting to false"""
684+
p = admin_pb2.AdminMessage()
685+
p.remove_fixed_position = True
686+
logging.info(f"Telling node to remove fixed position")
687+
688+
return self._sendAdmin(p)
689+
658690
def _fixupChannels(self):
659691
"""Fixup indexes and add disabled channels as needed"""
660692

meshtastic/tests/test_main.py

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -734,28 +734,22 @@ def test_main_setlat(capsys):
734734

735735
mocked_node = MagicMock(autospec=Node)
736736

737-
def mock_writeConfig():
738-
print("inside mocked writeConfig")
737+
def mock_setFixedPosition(lat, lon, alt):
738+
print("inside mocked setFixedPosition")
739+
print(f"{lat} {lon} {alt}")
739740

740-
mocked_node.writeConfig.side_effect = mock_writeConfig
741+
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
741742

742743
iface = MagicMock(autospec=SerialInterface)
743-
744-
def mock_sendPosition(lat, lon, alt):
745-
print("inside mocked sendPosition")
746-
print(f"{lat} {lon} {alt}")
747-
748-
iface.sendPosition.side_effect = mock_sendPosition
749-
iface.localNode.return_value = mocked_node
744+
iface.localNode = mocked_node
750745

751746
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
752747
main()
753748
out, err = capsys.readouterr()
754749
assert re.search(r"Connected to radio", out, re.MULTILINE)
755750
assert re.search(r"Fixing latitude", out, re.MULTILINE)
756751
assert re.search(r"Setting device position", out, re.MULTILINE)
757-
assert re.search(r"inside mocked sendPosition", out, re.MULTILINE)
758-
# TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
752+
assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE)
759753
assert err == ""
760754
mo.assert_called()
761755

@@ -769,28 +763,22 @@ def test_main_setlon(capsys):
769763

770764
mocked_node = MagicMock(autospec=Node)
771765

772-
def mock_writeConfig():
773-
print("inside mocked writeConfig")
766+
def mock_setFixedPosition(lat, lon, alt):
767+
print("inside mocked setFixedPosition")
768+
print(f"{lat} {lon} {alt}")
774769

775-
mocked_node.writeConfig.side_effect = mock_writeConfig
770+
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
776771

777772
iface = MagicMock(autospec=SerialInterface)
778-
779-
def mock_sendPosition(lat, lon, alt):
780-
print("inside mocked sendPosition")
781-
print(f"{lat} {lon} {alt}")
782-
783-
iface.sendPosition.side_effect = mock_sendPosition
784-
iface.localNode.return_value = mocked_node
773+
iface.localNode = mocked_node
785774

786775
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
787776
main()
788777
out, err = capsys.readouterr()
789778
assert re.search(r"Connected to radio", out, re.MULTILINE)
790779
assert re.search(r"Fixing longitude", out, re.MULTILINE)
791780
assert re.search(r"Setting device position", out, re.MULTILINE)
792-
assert re.search(r"inside mocked sendPosition", out, re.MULTILINE)
793-
# TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
781+
assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE)
794782
assert err == ""
795783
mo.assert_called()
796784

@@ -804,28 +792,22 @@ def test_main_setalt(capsys):
804792

805793
mocked_node = MagicMock(autospec=Node)
806794

807-
def mock_writeConfig():
808-
print("inside mocked writeConfig")
795+
def mock_setFixedPosition(lat, lon, alt):
796+
print("inside mocked setFixedPosition")
797+
print(f"{lat} {lon} {alt}")
809798

810-
mocked_node.writeConfig.side_effect = mock_writeConfig
799+
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
811800

812801
iface = MagicMock(autospec=SerialInterface)
813-
814-
def mock_sendPosition(lat, lon, alt):
815-
print("inside mocked sendPosition")
816-
print(f"{lat} {lon} {alt}")
817-
818-
iface.sendPosition.side_effect = mock_sendPosition
819-
iface.localNode.return_value = mocked_node
802+
iface.localNode = mocked_node
820803

821804
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
822805
main()
823806
out, err = capsys.readouterr()
824807
assert re.search(r"Connected to radio", out, re.MULTILINE)
825808
assert re.search(r"Fixing altitude", out, re.MULTILINE)
826809
assert re.search(r"Setting device position", out, re.MULTILINE)
827-
assert re.search(r"inside mocked sendPosition", out, re.MULTILINE)
828-
# TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
810+
assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE)
829811
assert err == ""
830812
mo.assert_called()
831813

0 commit comments

Comments
 (0)