Skip to content
Open
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
100 changes: 100 additions & 0 deletions fritzconnection/cli/fritzwol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""
fritzwol.py

Module to wake up a single host via the Fritzbox built-in mechanism.
This can be helpful if the host to be woken up is in a different
broadcast domain/subnet than the client which tries to wake up.
CLI interface.

This module is part of the FritzConnection package.
https://github.com/kbr/fritzconnection
License: MIT (https://opensource.org/licenses/MIT)
Authors: Maik Töpfer, Chris Bräucker
"""

from fritzconnection.core.exceptions import (
FritzArgumentError,
FritzAuthorizationError,
FritzLookUpError,
)

from ..lib.fritzhosts import FritzHosts
from .utils import (
get_cli_arguments,
get_instance,
print_common_exception_message,
print_header,
)


def wake_host(fh, args):
"""
Either wakes a host directly by MAC address, which should even work for hosts not known.
Or it tries to find the given parameter in the device list to determine the MAC address.
"""

print(args)

if args.device_mac_address:
mac = args.device_mac_address

elif args.device_ip_address:
try:
host = fh.get_specific_host_entry_by_ip(args.device_ip_address)
except (FritzArgumentError, FritzLookUpError):
msg = f"Error: unknown IP {args.device_ip_address}"
raise FritzArgumentError(msg)
mac = host['NewMACAddress']

elif args.device_name:
device_name = args.device_name.lower()
for entry in fh.get_generic_host_entries():
if entry['NewHostName'].lower() == device_name:
mac = entry['NewMACAddress']
break
else:
msg = f"Error: unknown device name '{args.device_name}'"
raise FritzArgumentError(msg)

fh.wakeonlan_host(mac)
print(f"Waking {mac}")


def add_arguments(parser):
arggroup = parser.add_argument_group('Wake-on-LAN options', 'mutually exclusive arguments to reference the target host')
group = arggroup.add_mutually_exclusive_group(required=True)
group.add_argument('-m', '--device-mac',
dest='device_mac_address',
default=None,
help='MAC address of the device to wake up. Sends a WoL packet without checking device existence.'
)
group.add_argument('-I', '--device-ip',
dest='device_ip_address',
default=None,
help='IP address of the device to wake up. Checks against the list of known devices.'
)
group.add_argument('-n', '--device-name',
dest='device_name',
default=None,
help='Name of the device to wake up. Checks against the list of known devices.'
)


def execute():
arguments = get_cli_arguments(add_arguments)
fh = get_instance(FritzHosts, arguments)
print_header(fh)
wake_host(fh, arguments)


def main():
try:
execute()
except FritzAuthorizationError as err:
print_common_exception_message(err)
except FritzArgumentError as err:
print(err)


if __name__ == '__main__':
main()
7 changes: 7 additions & 0 deletions fritzconnection/lib/fritzhosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ def set_wakeonlan_status(self, mac_address: str, status: bool = False) -> None:
}
self._action("X_AVM-DE_SetAutoWakeOnLANByMACAddress", arguments=args)

def wakeonlan_host(self, mac_address: str) -> None:
"""
Triggers sending a wake on lan message with the given `mac_address`
on the local network. This method has no return value.
"""
self._action("X_AVM-DE_WakeOnLANByMACAddress", NewMACAddress=mac_address)

def set_host_name(self, mac_address: str, name: str) -> None:
"""
Sets the hostname of the device with the given `mac_address` to
Expand Down
45 changes: 45 additions & 0 deletions fritzconnection/tests/cli/test_fritzwol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from unittest.mock import Mock
from argparse import Namespace

from fritzconnection.cli.fritzwol import wake_host


def test_calls_wakeonlan_host_with_macaddress_directly():
mac = 'C0:FF:EE:C0:FF:EE'
fritz_host = Mock()

wake_host(fritz_host, Namespace(device_mac_address=mac, device_ip_address=None, device_name=None))
fritz_host.get_generic_host_entry.assert_not_called()
fritz_host.get_specific_host_entry_by_ip.assert_not_called()
fritz_host.get_generic_host_entries.assert_not_called()

fritz_host.wakeonlan_host.assert_called_with(mac)


def test_ip_calls_specific_host_then_wakeonlan():
mac = 'C0:FF:EE:C0:FF:EE'
fritz_host = Mock()
fritz_host.get_specific_host_entry_by_ip.return_value = {'NewMACAddress': mac}

wake_host(fritz_host, Namespace(device_ip_address='127.0.0.1', device_mac_address=None, device_name=None))
fritz_host.get_generic_host_entry.assert_not_called()
fritz_host.get_specific_host_entry_by_ip.assert_called_with('127.0.0.1')
fritz_host.get_generic_host_entries.assert_not_called()

fritz_host.wakeonlan_host.assert_called_with(mac)


def test_name_calls_generic_host_entries_then_wakeonlan():
mac = 'C0:FF:EE:C0:FF:EE'
fritz_host = Mock()
fritz_host.get_generic_host_entries.return_value = [
{'NewHostName': 'otherhost', 'NewMACAddress': '11:22:33:44:55:66'},
{'NewHostName': 'thishost', 'NewMACAddress': mac}
]

wake_host(fritz_host, Namespace(device_name='thishost', device_mac_address=None, device_ip_address=None))
fritz_host.get_generic_host_entry.assert_not_called()
fritz_host.get_specific_host_entry_by_ip.assert_not_called()
fritz_host.get_generic_host_entries.assert_called_once()

fritz_host.wakeonlan_host.assert_called_with(mac)
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def get_version():
"fritzphonebook = fritzconnection.cli.fritzphonebook:main",
"fritzstatus = fritzconnection.cli.fritzstatus:main",
"fritzwlan = fritzconnection.cli.fritzwlan:main",
"fritzwol = fritzconnection.cli.fritzwol:main",
]
},
)