Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use new fixed position admin messages and add --remove-position argument #584

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 36 additions & 15 deletions meshtastic/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,33 +257,41 @@
if not args.export_config:
print("Connected to radio")

if args.setlat or args.setlon or args.setalt:
if args.remove_position:
if args.dest != BROADCAST_ADDR:
print("Setting positions of remote nodes is not supported.")
return
closeNow = True
print("Removing fixed position and disabling fixed position setting")
interface.localNode.removeFixedPosition()

Check warning on line 266 in meshtastic/__main__.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/__main__.py#L261-L266

Added lines #L261 - L266 were not covered by tests
elif args.setlat or args.setlon or args.setalt:
if args.dest != BROADCAST_ADDR:
print("Setting latitude, longitude, and altitude of remote nodes is not supported.")
return
closeNow = True

alt = 0
lat = 0.0
lon = 0.0
localConfig = interface.localNode.localConfig
lat = 0
lon = 0
if args.setalt:
alt = int(args.setalt)
localConfig.position.fixed_position = True
print(f"Fixing altitude at {alt} meters")
if args.setlat:
lat = float(args.setlat)
localConfig.position.fixed_position = True
try:
lat = int(args.setlat)
except ValueError:
lat = float(args.setlat)
print(f"Fixing latitude at {lat} degrees")
if args.setlon:
lon = float(args.setlon)
localConfig.position.fixed_position = True
try:
lon = int(args.setlon)
except ValueError:
lon = float(args.setlon)
print(f"Fixing longitude at {lon} degrees")

print("Setting device position")
print("Setting device position and enabling fixed position setting")
# can include lat/long/alt etc: latitude = 37.5, longitude = -122.1
interface.sendPosition(lat, lon, alt)
interface.localNode.writeConfig("position")
interface.localNode.setFixedPosition(lat, lon, alt)
elif not args.no_time:
# We normally provide a current time to the mesh when we connect
if interface.localNode.nodeNum in interface.nodesByNum and "position" in interface.nodesByNum[interface.localNode.nodeNum]:
Expand Down Expand Up @@ -1445,12 +1453,25 @@
action="store_true",
)

group.add_argument("--setalt", help="Set device altitude in meters (allows use without GPS)")
group.add_argument(
"--setalt",
help="Set device altitude in meters (allows use without GPS), and enable fixed position.",
)

group.add_argument("--setlat", help="Set device latitude (allows use without GPS)")
group.add_argument(
"--setlat",
help="Set device latitude (allows use without GPS), and enable fixed position. Accepts a decimal value or an integer premultiplied by 1e7.",
)

group.add_argument(
"--setlon",
help="Set device longitude (allows use without GPS), and enable fixed position. Accepts a decimal value or an integer premultiplied by 1e7.",
)

group.add_argument(
"--setlon", help="Set device longitude (allows use without GPS)"
"--remove-position",
help="Clear any existing fixed position and disable fixed position.",
action="store_true",
)

group.add_argument(
Expand Down
34 changes: 33 additions & 1 deletion meshtastic/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Union

from meshtastic import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, portnums_pb2
from meshtastic import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2
from meshtastic.util import (
Timeout,
camel_to_snake,
Expand Down Expand Up @@ -655,6 +655,38 @@
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)

def setFixedPosition(self, lat: Union[int, float], lon: Union[int, float], alt: int):
"""Tell the node to set fixed position to the provided value and enable the fixed position setting"""
if self != self.iface.localNode:
logging.error("Setting position of remote nodes is not supported.")
return None

Check warning on line 662 in meshtastic/node.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/node.py#L660-L662

Added lines #L660 - L662 were not covered by tests

p = mesh_pb2.Position()
if isinstance(lat, float) and lat != 0.0:
p.latitude_i = int(lat / 1e-7)
elif isinstance(lat, int) and lat != 0:
p.latitude_i = lat

Check warning on line 668 in meshtastic/node.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/node.py#L664-L668

Added lines #L664 - L668 were not covered by tests

if isinstance(lon, float) and lon != 0.0:
p.longitude_i = int(lon / 1e-7)
elif isinstance(lon, int) and lon != 0:
p.longitude_i = lon

Check warning on line 673 in meshtastic/node.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/node.py#L670-L673

Added lines #L670 - L673 were not covered by tests

if alt != 0:
p.altitude = alt

Check warning on line 676 in meshtastic/node.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/node.py#L675-L676

Added lines #L675 - L676 were not covered by tests

a = admin_pb2.AdminMessage()
a.set_fixed_position.CopyFrom(p)
return self._sendAdmin(a)

Check warning on line 680 in meshtastic/node.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/node.py#L678-L680

Added lines #L678 - L680 were not covered by tests

def removeFixedPosition(self):
"""Tell the node to remove the fixed position and set the fixed position setting to false"""
p = admin_pb2.AdminMessage()
p.remove_fixed_position = True
logging.info(f"Telling node to remove fixed position")

Check warning on line 686 in meshtastic/node.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/node.py#L684-L686

Added lines #L684 - L686 were not covered by tests

return self._sendAdmin(p)

Check warning on line 688 in meshtastic/node.py

View check run for this annotation

Codecov / codecov/patch

meshtastic/node.py#L688

Added line #L688 was not covered by tests

def _fixupChannels(self):
"""Fixup indexes and add disabled channels as needed"""

Expand Down
54 changes: 18 additions & 36 deletions meshtastic/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,28 +734,22 @@ def test_main_setlat(capsys):

mocked_node = MagicMock(autospec=Node)

def mock_writeConfig():
print("inside mocked writeConfig")
def mock_setFixedPosition(lat, lon, alt):
print("inside mocked setFixedPosition")
print(f"{lat} {lon} {alt}")

mocked_node.writeConfig.side_effect = mock_writeConfig
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition

iface = MagicMock(autospec=SerialInterface)

def mock_sendPosition(lat, lon, alt):
print("inside mocked sendPosition")
print(f"{lat} {lon} {alt}")

iface.sendPosition.side_effect = mock_sendPosition
iface.localNode.return_value = mocked_node
iface.localNode = mocked_node

with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"Fixing latitude", out, re.MULTILINE)
assert re.search(r"Setting device position", out, re.MULTILINE)
assert re.search(r"inside mocked sendPosition", out, re.MULTILINE)
# TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE)
assert err == ""
mo.assert_called()

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

mocked_node = MagicMock(autospec=Node)

def mock_writeConfig():
print("inside mocked writeConfig")
def mock_setFixedPosition(lat, lon, alt):
print("inside mocked setFixedPosition")
print(f"{lat} {lon} {alt}")

mocked_node.writeConfig.side_effect = mock_writeConfig
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition

iface = MagicMock(autospec=SerialInterface)

def mock_sendPosition(lat, lon, alt):
print("inside mocked sendPosition")
print(f"{lat} {lon} {alt}")

iface.sendPosition.side_effect = mock_sendPosition
iface.localNode.return_value = mocked_node
iface.localNode = mocked_node

with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"Fixing longitude", out, re.MULTILINE)
assert re.search(r"Setting device position", out, re.MULTILINE)
assert re.search(r"inside mocked sendPosition", out, re.MULTILINE)
# TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE)
assert err == ""
mo.assert_called()

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

mocked_node = MagicMock(autospec=Node)

def mock_writeConfig():
print("inside mocked writeConfig")
def mock_setFixedPosition(lat, lon, alt):
print("inside mocked setFixedPosition")
print(f"{lat} {lon} {alt}")

mocked_node.writeConfig.side_effect = mock_writeConfig
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition

iface = MagicMock(autospec=SerialInterface)

def mock_sendPosition(lat, lon, alt):
print("inside mocked sendPosition")
print(f"{lat} {lon} {alt}")

iface.sendPosition.side_effect = mock_sendPosition
iface.localNode.return_value = mocked_node
iface.localNode = mocked_node

with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"Fixing altitude", out, re.MULTILINE)
assert re.search(r"Setting device position", out, re.MULTILINE)
assert re.search(r"inside mocked sendPosition", out, re.MULTILINE)
# TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE)
assert err == ""
mo.assert_called()

Expand Down
Loading