diff --git a/docs/source/samples/bootloader/poe_set_ip.rst b/docs/source/samples/bootloader/poe_set_ip.rst index a8c9867f5..fac5a8f17 100644 --- a/docs/source/samples/bootloader/poe_set_ip.rst +++ b/docs/source/samples/bootloader/poe_set_ip.rst @@ -1,7 +1,7 @@ POE set IP ========== -This script allows you to set static or dynamic IP, or clear bootloader config on your OAK-POE device. +This script allows you to set static or dynamic IP, get the currently set IP, or clear bootloader config on your OAK-POE device. .. warning:: Make sure to **set mask and gateway correctly!** If they are set incorrectly you will soft-brick your @@ -9,37 +9,36 @@ This script allows you to set static or dynamic IP, or clear bootloader config o your OAK PoE. .. note:: + The script currently doesn't support setting a IPv6 address. We suggest using :ref:`Device Manager`, a GUI tool for interfacing with the bootloader and its configurations. Demo #### -Example script output: - +Use cases: .. code-block:: bash + # Set a static IPv4 + python3 poe_set_ip.py static + # Set a static IPv4 and gateway + python3 poe_set_ip.py static --gateway + # Set a dynamic IPv4 and gateway + python3 poe_set_ip.py dynamic --gateway + # Get the currently set IPv4 + python3 poe_set_ip.py get - Found device with name: 192.168.1.136 - ------------------------------------- - "1" to set a static IPv4 address - "2" to set a dynamic IPv4 address - "3" to clear the config - 1 +Example of how to set a static IPv4 address: + +.. code-block:: bash + python3 poe_set_ip.py static 192.168.1.200 255.255.255.0 --gateway 192.168.1.1 ------------------------------------- - Enter IPv4: 192.168.1.200 - Enter IPv4 Mask: 255.255.255.0 - Enter IPv4 Gateway: 192.168.1.1 - Flashing static IPv4 192.168.1.200, mask 255.255.255.0, gateway 192.168.1.1 to the POE device. Enter 'y' to confirm. y Flashing successful. -If you run the same example again after 10 seconds, you will see that IP changed to **192.168.1.200**: +If you run the same example again after 10 seconds, you verify that the IP was changed to **192.168.1.200**: .. code-block:: bash - - Found device with name: 192.168.1.200 + python3 poe_set_ip.py get ------------------------------------- - "1" to set a static IPv4 address - "2" to set a dynamic IPv4 address - "3" to clear the config + Device (IPv4): 192.168.1.200, Mask: 255.255.255.0, Gateway: 192.168.1.1 You can now also use the `Manually specify device IP `__ script and change the IP to :code:`192.168.1.200`. diff --git a/examples/bootloader/poe_set_ip.py b/examples/bootloader/poe_set_ip.py index 7314dacd6..14ecc89a5 100755 --- a/examples/bootloader/poe_set_ip.py +++ b/examples/bootloader/poe_set_ip.py @@ -1,45 +1,112 @@ import depthai as dai -(found, info) = dai.DeviceBootloader.getFirstAvailableDevice() - -def check_str(s: str): - spl = s.split(".") - if len(spl) != 4: - raise ValueError(f"Entered value {s} doesn't contain 3 dots. Value has to be in the following format: '255.255.255.255'") - for num in spl: - if 255 < int(num): - raise ValueError("Entered values can't be above 255!") - return s - -if found: - print(f'Found device with name: {info.name}') - print('-------------------------------------') - print('"1" to set a static IPv4 address') - print('"2" to set a dynamic IPv4 address') - print('"3" to clear the config') - key = input('Enter the number: ').strip() - print('-------------------------------------') - if int(key) < 1 or 3 < int(key): - raise ValueError("Entered value should either be '1', '2' or '3'!") +import argparse +from enum import Enum +from ipaddress import ip_address, ip_network - with dai.DeviceBootloader(info) as bl: - if key in ['1', '2']: - ipv4 = check_str(input("Enter IPv4: ").strip()) - mask = check_str(input("Enter IPv4 Mask: ").strip()) - gateway = check_str(input("Enter IPv4 Gateway: ").strip()) - mode = 'static' if key == '1' else 'dynamic' - val = input(f"Flashing {mode} IPv4 {ipv4}, mask {mask}, gateway {gateway} to the POE device. Enter 'y' to confirm. ").strip() - if val != 'y': - raise Exception("Flashing aborted.") +class Modes(str, Enum): + IP_STATIC = "static" + IP_DYNAMIC = "dynamic" + CLEAR = "clear" + GET = "get" + + +def str_to_ip(ip: str) -> int: + """Converts an IPv4 string to an integer.""" + return sum([int(x) << (8 * i) for i, x in enumerate(ip.split("."))]) + + +def main(mode: Modes, ip: str = "", mask: str = "", gateway: str = "") -> None: + (found, info) = dai.DeviceBootloader.getFirstAvailableDevice() + if not found: + raise ValueError("No device found!") + # TODO: Support IPv6 + with dai.DeviceBootloader(info) as bl: + try: + # Read config, if it exists + conf = bl.readConfig() + except RuntimeError: + # Create a default config if one doesn't exist conf = dai.DeviceBootloader.Config() - if key == '1': conf.setStaticIPv4(ipv4, mask, gateway) - elif key == '2': conf.setDynamicIPv4(ipv4, mask, gateway) + + if mode == Modes.GET: + print( + f"Device (IPv4): {conf.getIPv4()}, Mask: {conf.getIPv4Mask()}, Gateway: {conf.getIPv4Gateway()}" + ) + return + if mode in {Modes.IP_STATIC, Modes.IP_DYNAMIC}: + is_ip_static = mode == Modes.IP_STATIC + if not gateway: + conf_d = conf.toJson() + conf_d.update( + { + "network": { + "ipv4": str_to_ip(ip), + "ipv4Mask": str_to_ip(mask), + "staticIpv4": is_ip_static, + } + } + ) + conf = dai.DeviceBootloader.Config.fromJson(conf_d) + else: + set_ip = conf.setStaticIPv4 if is_ip_static else conf.setDynamicIPv4 + set_ip(ip, mask, gateway) (success, error) = bl.flashConfig(conf) - elif key == '3': + else: (success, error) = bl.flashConfigClear() if not success: print(f"Flashing failed: {error}") else: - print(f"Flashing successful.") \ No newline at end of file + print(f"Flashing successful.") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + prog="PoE set IP", + description="You can set a static, dynamic IPv4 of the device or clear the flash config.", + ) + subparsers = parser.add_subparsers(dest="mode", required=True, help="Choose a mode") + validate_ip = lambda ip: str(ip_address(ip)) + + ip_parser = subparsers.add_parser( + Modes.IP_STATIC.value, description="Set a static IPv4 address" + ) + ip_parser.add_argument("ip", type=validate_ip, help="Device IPv4 address") + ip_parser.add_argument("mask", type=validate_ip, help="Network mask") + ip_parser.add_argument( + "-g", "--gateway", type=validate_ip, help="Gateway IPv4 address" + ) + ip_parser = subparsers.add_parser( + Modes.IP_DYNAMIC.value, description="Set a dynamic IPv4 address" + ) + ip_parser.add_argument("ip", type=validate_ip, help="Device IPv4 address") + ip_parser.add_argument("mask", type=validate_ip, help="Network mask") + ip_parser.add_argument( + "-g", "--gateway", type=validate_ip, help="Gateway IPv4 address" + ) + ip_parser = subparsers.add_parser( + Modes.CLEAR.value, description="Clear flash configuration" + ) + ip_parser = subparsers.add_parser( + Modes.GET.value, description="Get IP configuration" + ) + args = parser.parse_args() + + # Check if device IP is on the same network as the gateway + if ( + hasattr(args, "gateway") + and args.gateway + and args.mode in {Modes.IP_STATIC, Modes.IP_DYNAMIC} + ): + device_ip = ip_address(args.ip) + mask = ip_address(args.mask) + gateway = ip_address(args.gateway) + + network_ip = ip_address(int(gateway) & int(mask)) + network = ip_network(f"{network_ip}/{mask}") + if not device_ip in network: + raise ValueError("Device IP doesn't belong in the same network as gateway!") + + main(**vars(args))