eg.wakeonlan 1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2018, Eugene Gershnik
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of the copyright holder nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.1
2
+ Name: eg.wakeonlan
3
+ Version: 1.1
4
+ Summary: Yet another wake-on-lan library
5
+ Author-email: Eugene Gershnik <gershnik@hotmail.com>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/gershnik/wakeonlan
8
+ Keywords: wakeonlan
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: Operating System :: MacOS :: MacOS X
12
+ Classifier: Operating System :: Microsoft
13
+ Classifier: Operating System :: POSIX
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Topic :: System
18
+ Classifier: Topic :: System :: Networking
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.7
22
+ Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.7
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE.txt
31
+
32
+ # wakeonlan #
33
+
34
+ Yet another wake-on-lan command line script.
35
+
36
+ ## Why another one?
37
+
38
+ I couldn't find one that worked and did what I need. Specifically I need:
39
+ * A command line utility that works on Mac, Windows and Linux
40
+ * Can use saved configurations rather than force me to remember the MAC addresses of the machines I need to wake.
41
+ * Ideally, let me manipulate (create, delete, update, list) saved configurations using the same utility.
42
+ * Ideally, be open source so I can see what it is doing and know it doesn't do anything nefarious
43
+
44
+ None of the existing tools I found satisfied these criteria (even without the last two) so I wrote my own.
45
+
46
+ ### Pre-requisites
47
+
48
+ Python 3.7 or above. No additional packages required.
49
+
50
+ ### Installation
51
+
52
+ ```bash
53
+ pip3 install eg.wakeonlan
54
+ ```
55
+
56
+ ### Usage
57
+
58
+
59
+ #### Wake up a machine given its MAC address XX:XX:XX:XX:XX:XX
60
+
61
+ ```bash
62
+ wakeonlan XX:XX:XX:XX:XX:XX [-a BroadcastAddress] [-p Port]
63
+ ```
64
+
65
+ If not specified BroadcastAddress is 255.255.255.255 and Port is 9
66
+
67
+ #### Save wake up configuration to be used later
68
+
69
+ ```bash
70
+ wakeonlan --save Name XX:XX:XX:XX:XX:XX [-a BroadcastAddress] [-p Port]
71
+ ```
72
+
73
+ Name can be anything. The configuration is saved into `$HOME/.wakeonlan` file in JSON format
74
+ --save can be abbreviated as -s
75
+
76
+ #### Wake up a machine given saved configuration name
77
+
78
+ ```bash
79
+ wakeonlan Name
80
+ ```
81
+
82
+ #### List existing configuration names
83
+
84
+ ```bash
85
+ wakeonlan --list
86
+ ```
87
+
88
+ `--list` can be abbreviated as `-l`
89
+
90
+ #### Delete a configuration
91
+
92
+ ```bash
93
+ wakeonlan --delete Name
94
+ ```
95
+
96
+ `--delete` can be abbreviated as `-d`
97
+
98
+ ### Transferring configurations to another machine
99
+
100
+ Saved configurations are stored in `$HOME/.wakeonlan` file (`%USERPROFILE%\.wakeonlan` for Windows users).
101
+ Copy this file to another machine into equivalent location to transfer all the configurations.
102
+
103
+
104
+
105
+
@@ -0,0 +1,74 @@
1
+ # wakeonlan #
2
+
3
+ Yet another wake-on-lan command line script.
4
+
5
+ ## Why another one?
6
+
7
+ I couldn't find one that worked and did what I need. Specifically I need:
8
+ * A command line utility that works on Mac, Windows and Linux
9
+ * Can use saved configurations rather than force me to remember the MAC addresses of the machines I need to wake.
10
+ * Ideally, let me manipulate (create, delete, update, list) saved configurations using the same utility.
11
+ * Ideally, be open source so I can see what it is doing and know it doesn't do anything nefarious
12
+
13
+ None of the existing tools I found satisfied these criteria (even without the last two) so I wrote my own.
14
+
15
+ ### Pre-requisites
16
+
17
+ Python 3.7 or above. No additional packages required.
18
+
19
+ ### Installation
20
+
21
+ ```bash
22
+ pip3 install eg.wakeonlan
23
+ ```
24
+
25
+ ### Usage
26
+
27
+
28
+ #### Wake up a machine given its MAC address XX:XX:XX:XX:XX:XX
29
+
30
+ ```bash
31
+ wakeonlan XX:XX:XX:XX:XX:XX [-a BroadcastAddress] [-p Port]
32
+ ```
33
+
34
+ If not specified BroadcastAddress is 255.255.255.255 and Port is 9
35
+
36
+ #### Save wake up configuration to be used later
37
+
38
+ ```bash
39
+ wakeonlan --save Name XX:XX:XX:XX:XX:XX [-a BroadcastAddress] [-p Port]
40
+ ```
41
+
42
+ Name can be anything. The configuration is saved into `$HOME/.wakeonlan` file in JSON format
43
+ --save can be abbreviated as -s
44
+
45
+ #### Wake up a machine given saved configuration name
46
+
47
+ ```bash
48
+ wakeonlan Name
49
+ ```
50
+
51
+ #### List existing configuration names
52
+
53
+ ```bash
54
+ wakeonlan --list
55
+ ```
56
+
57
+ `--list` can be abbreviated as `-l`
58
+
59
+ #### Delete a configuration
60
+
61
+ ```bash
62
+ wakeonlan --delete Name
63
+ ```
64
+
65
+ `--delete` can be abbreviated as `-d`
66
+
67
+ ### Transferring configurations to another machine
68
+
69
+ Saved configurations are stored in `$HOME/.wakeonlan` file (`%USERPROFILE%\.wakeonlan` for Windows users).
70
+ Copy this file to another machine into equivalent location to transfer all the configurations.
71
+
72
+
73
+
74
+
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2018, Eugene Gershnik
2
+ # Use of this source code is governed by a BSD-style
3
+ # license that can be found in the LICENSE.txt file or at
4
+ # https://opensource.org/licenses/BSD-3-Clause
5
+
6
+ [build-system]
7
+ requires = [
8
+ "setuptools>=61"
9
+ ]
10
+ build-backend = "setuptools.build_meta"
11
+
12
+ [tool.setuptools]
13
+ include-package-data = false
14
+
15
+ [tool.setuptools.dynamic]
16
+ version = {attr = "wakeonlan.__version__"}
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["src"]
20
+
21
+ [project]
22
+ name = "eg.wakeonlan"
23
+ dynamic = ["version"]
24
+ description="Yet another wake-on-lan library"
25
+ readme="README.md"
26
+ authors= [
27
+ { name = 'Eugene Gershnik', email='gershnik@hotmail.com'}
28
+ ]
29
+ requires-python = ">=3.7"
30
+ keywords = ["wakeonlan"]
31
+ license = {text = "BSD-3-Clause"}
32
+ classifiers = [
33
+ "Development Status :: 5 - Production/Stable",
34
+
35
+ "License :: OSI Approved :: BSD License",
36
+
37
+ "Operating System :: MacOS :: MacOS X",
38
+ "Operating System :: Microsoft",
39
+ "Operating System :: POSIX",
40
+
41
+ "Environment :: Console",
42
+
43
+ "Intended Audience :: End Users/Desktop",
44
+ "Intended Audience :: System Administrators",
45
+
46
+ "Topic :: System",
47
+ "Topic :: System :: Networking",
48
+
49
+ "Programming Language :: Python",
50
+ "Programming Language :: Python :: 3",
51
+ "Programming Language :: Python :: 3.7",
52
+ "Programming Language :: Python :: 3.8",
53
+ "Programming Language :: Python :: 3.9",
54
+ "Programming Language :: Python :: 3.10",
55
+ "Programming Language :: Python :: 3.11",
56
+ "Programming Language :: Python :: 3.12",
57
+
58
+ "Typing :: Typed"
59
+ ]
60
+
61
+ [project.urls]
62
+ Homepage = 'https://github.com/gershnik/wakeonlan'
63
+
64
+
65
+ [project.scripts]
66
+ wakeonlan = "wakeonlan.wakeonlan:main"
67
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.1
2
+ Name: eg.wakeonlan
3
+ Version: 1.1
4
+ Summary: Yet another wake-on-lan library
5
+ Author-email: Eugene Gershnik <gershnik@hotmail.com>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/gershnik/wakeonlan
8
+ Keywords: wakeonlan
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: Operating System :: MacOS :: MacOS X
12
+ Classifier: Operating System :: Microsoft
13
+ Classifier: Operating System :: POSIX
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Topic :: System
18
+ Classifier: Topic :: System :: Networking
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.7
22
+ Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.7
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE.txt
31
+
32
+ # wakeonlan #
33
+
34
+ Yet another wake-on-lan command line script.
35
+
36
+ ## Why another one?
37
+
38
+ I couldn't find one that worked and did what I need. Specifically I need:
39
+ * A command line utility that works on Mac, Windows and Linux
40
+ * Can use saved configurations rather than force me to remember the MAC addresses of the machines I need to wake.
41
+ * Ideally, let me manipulate (create, delete, update, list) saved configurations using the same utility.
42
+ * Ideally, be open source so I can see what it is doing and know it doesn't do anything nefarious
43
+
44
+ None of the existing tools I found satisfied these criteria (even without the last two) so I wrote my own.
45
+
46
+ ### Pre-requisites
47
+
48
+ Python 3.7 or above. No additional packages required.
49
+
50
+ ### Installation
51
+
52
+ ```bash
53
+ pip3 install eg.wakeonlan
54
+ ```
55
+
56
+ ### Usage
57
+
58
+
59
+ #### Wake up a machine given its MAC address XX:XX:XX:XX:XX:XX
60
+
61
+ ```bash
62
+ wakeonlan XX:XX:XX:XX:XX:XX [-a BroadcastAddress] [-p Port]
63
+ ```
64
+
65
+ If not specified BroadcastAddress is 255.255.255.255 and Port is 9
66
+
67
+ #### Save wake up configuration to be used later
68
+
69
+ ```bash
70
+ wakeonlan --save Name XX:XX:XX:XX:XX:XX [-a BroadcastAddress] [-p Port]
71
+ ```
72
+
73
+ Name can be anything. The configuration is saved into `$HOME/.wakeonlan` file in JSON format
74
+ --save can be abbreviated as -s
75
+
76
+ #### Wake up a machine given saved configuration name
77
+
78
+ ```bash
79
+ wakeonlan Name
80
+ ```
81
+
82
+ #### List existing configuration names
83
+
84
+ ```bash
85
+ wakeonlan --list
86
+ ```
87
+
88
+ `--list` can be abbreviated as `-l`
89
+
90
+ #### Delete a configuration
91
+
92
+ ```bash
93
+ wakeonlan --delete Name
94
+ ```
95
+
96
+ `--delete` can be abbreviated as `-d`
97
+
98
+ ### Transferring configurations to another machine
99
+
100
+ Saved configurations are stored in `$HOME/.wakeonlan` file (`%USERPROFILE%\.wakeonlan` for Windows users).
101
+ Copy this file to another machine into equivalent location to transfer all the configurations.
102
+
103
+
104
+
105
+
@@ -0,0 +1,13 @@
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ src/eg.wakeonlan.egg-info/PKG-INFO
5
+ src/eg.wakeonlan.egg-info/SOURCES.txt
6
+ src/eg.wakeonlan.egg-info/dependency_links.txt
7
+ src/eg.wakeonlan.egg-info/entry_points.txt
8
+ src/eg.wakeonlan.egg-info/top_level.txt
9
+ src/wakeonlan/__init__.py
10
+ src/wakeonlan/__main__.py
11
+ src/wakeonlan/py.typed
12
+ src/wakeonlan/wakeonlan.py
13
+ tests/test_wakeonlan.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ wakeonlan = wakeonlan.wakeonlan:main
@@ -0,0 +1 @@
1
+ wakeonlan
@@ -0,0 +1,8 @@
1
+ # Copyright (c) 2018, Eugene Gershnik
2
+ # Use of this source code is governed by a BSD-style
3
+ # license that can be found in the LICENSE.txt file or at
4
+ # https://opensource.org/licenses/BSD-3-Clause
5
+
6
+ """wakeonlan package"""
7
+
8
+ from .wakeonlan import VERSION as __version__, wake, saveName, getNameRecord, getNames, deleteName
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2018, Eugene Gershnik
2
+ # Use of this source code is governed by a BSD-style
3
+ # license that can be found in the LICENSE.txt file or at
4
+ # https://opensource.org/licenses/BSD-3-Clause
5
+
6
+ import sys
7
+ from .wakeonlan import main
8
+
9
+ sys.exit(main())
File without changes
@@ -0,0 +1,275 @@
1
+ # Copyright (c) 2018, Eugene Gershnik
2
+ # Use of this source code is governed by a BSD-style
3
+ # license that can be found in the LICENSE.txt file or at
4
+ # https://opensource.org/licenses/BSD-3-Clause
5
+
6
+ import sys
7
+ import os
8
+ import socket
9
+ import argparse
10
+ import re
11
+ import json
12
+ import shutil
13
+ from pathlib import Path
14
+ from typing import Any, Dict, Sequence, Tuple, Union
15
+
16
+ VERSION = '1.1'
17
+
18
+ PROG = 'wakeonlan'
19
+
20
+ DESCRIPTION = 'Send Wake-On-Lan packet to a given machine'
21
+
22
+ USAGE = r'''
23
+ %(prog)s MAC [-a IPADDR] [-p PORT]
24
+ %(prog)s NAME
25
+ %(prog)s --save NAME MAC [-a IPADDR] [-p PORT]
26
+ %(prog)s --delete NAME
27
+ %(prog)s --list
28
+ %(prog)s --version
29
+ %(prog)s --help
30
+ '''
31
+
32
+
33
+ DEFAULT_IP = '255.255.255.255'
34
+ DEFAULT_PORT = 9
35
+ CONFIG_HOME = Path(os.environ.get('WAKEONLAN_HOME', Path.home()))
36
+ CONFIG_PATH = CONFIG_HOME / '.wakeonlan'
37
+ CONFIG_TMP_PATH = CONFIG_HOME /'.wakeonlan.tmp'
38
+ MAC_PATTERN = re.compile(r'[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}')
39
+ IP_PATTERN = re.compile(r'(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)')
40
+
41
+
42
+ WAKE_CMD = 1
43
+ WAKE_BY_NAME_CMD = 2
44
+ SAVE_CMD = 3
45
+ DELETE_CMD = 4
46
+ LIST_CMD = 5
47
+
48
+ MacAddress = Sequence[int]
49
+ IPAddress = str
50
+ Port = int
51
+ SocketAddress = Union[Tuple[Any, ...], str, Any] #see socket._Address
52
+ HostRecord = Tuple[MacAddress, Tuple[IPAddress, Port]]
53
+
54
+ class WakeOnLanError(Exception):
55
+ def __init__(self, message):
56
+ super().__init__(message)
57
+
58
+ def splitMac(mac):
59
+ return [int(x, 16) for x in mac.split(':')]
60
+
61
+ def joinMac(macItems):
62
+ return ':'.join([f'{x:02X}' for x in macItems])
63
+
64
+ def parseArgs():
65
+
66
+ def mac_address_or_name(string):
67
+ if MAC_PATTERN.match(string):
68
+ return splitMac(string)
69
+ return string
70
+
71
+ def ip_address(string):
72
+ if not IP_PATTERN.match(string):
73
+ raise argparse.ArgumentTypeError('invalid IPv4 address ' + string)
74
+ return string
75
+
76
+ def port(string):
77
+ try:
78
+ val = int(string)
79
+ if val < 0 or val >= 65535:
80
+ raise argparse.ArgumentTypeError('invalid port ' + string)
81
+ return val
82
+ except ValueError:
83
+ raise argparse.ArgumentTypeError('invalid port ' + string)
84
+
85
+ def exitWithMessage(parser, message):
86
+ print(message, file=sys.stderr)
87
+ parser.print_usage()
88
+ sys.exit(1)
89
+
90
+ parser = argparse.ArgumentParser(description=DESCRIPTION, usage=USAGE, add_help=False,
91
+ prog=PROG)
92
+ argsGroup = parser.add_argument_group('arguments')
93
+ argsGroup.add_argument('macOrName', type=mac_address_or_name, nargs='?', metavar='MAC or NAME',
94
+ help='''MAC address or saved name of the machine to wake.
95
+ MAC address must be in XX:XX:XX:XX:XX:XX format''')
96
+ flagsGroup = parser.add_argument_group('switches')
97
+ flagsGroup.add_argument('-a', dest='ipaddr', type=ip_address,
98
+ help='Broadcast IPv4 address. This is NOT the IP address of the machine')
99
+ flagsGroup.add_argument('-p', dest='port', type=port,
100
+ help='Wake-On-Lan port')
101
+ manageGroud = flagsGroup.add_mutually_exclusive_group()
102
+ manageGroud.add_argument('--save', '-s', type=str, dest='saveName', metavar='NAME',
103
+ help='Save wake arguments as NAME')
104
+ manageGroud.add_argument('--delete', '-d', type=str, dest='deleteName', metavar='NAME',
105
+ help='Delete saved NAME')
106
+ manageGroud.add_argument('--list', '-l', action='store_true', dest='listNames',
107
+ help='List saved names')
108
+ flagsGroup.add_argument('--version', action='version', version=f'%(prog)s {VERSION}')
109
+ flagsGroup.add_argument('--help', '-h', action='help',
110
+ help='show this help message and exit')
111
+ parser.set_defaults(cmd=0)
112
+
113
+ args = parser.parse_args()
114
+
115
+ if not args.saveName is None:
116
+ if not type(args.macOrName) is list:
117
+ exitWithMessage(parser, 'Must specify MAC address to save')
118
+ args.cmd = SAVE_CMD
119
+ elif not args.deleteName is None:
120
+ if not args.macOrName is None:
121
+ exitWithMessage(parser, 'parameter MAC_OR_NAME: not allowed with --delete/-d')
122
+ if not args.ipaddr is None:
123
+ exitWithMessage(parser, 'argument -a: not allowed with argument with --delete/-d')
124
+ if not args.port is None:
125
+ exitWithMessage(parser, 'argument -p: not allowed with argument with --delete/-d')
126
+ args.cmd = DELETE_CMD
127
+ elif args.listNames:
128
+ if not args.macOrName is None:
129
+ exitWithMessage(parser, 'parameter MAC_OR_NAME: not allowed with --list/-l')
130
+ if not args.ipaddr is None:
131
+ exitWithMessage(parser, 'argument -a: not allowed with argument with --list/-l')
132
+ if not args.port is None:
133
+ exitWithMessage(parser, 'argument -p: not allowed with argument with --list/-l')
134
+ args.cmd = LIST_CMD
135
+
136
+ if args.cmd == 0:
137
+ if args.macOrName is None:
138
+ exitWithMessage(parser, 'MAC or name is required')
139
+ if type(args.macOrName) is list:
140
+ args.cmd = WAKE_CMD
141
+ else:
142
+ if not args.ipaddr is None:
143
+ exitWithMessage(parser, 'Cannot specify broadcast address with name')
144
+ if not args.port is None:
145
+ exitWithMessage(parser, 'Cannot specify port with name')
146
+ args.cmd = WAKE_BY_NAME_CMD
147
+
148
+ if args.cmd == SAVE_CMD or args.cmd == WAKE_CMD:
149
+ args.ipaddr = DEFAULT_IP if args.ipaddr is None else args.ipaddr
150
+ args.port = DEFAULT_PORT if args.port is None else args.port
151
+
152
+ return args
153
+
154
+ def wake(mac: MacAddress, addr: SocketAddress) -> None :
155
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
156
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
157
+
158
+ payload = bytearray(17 * 6)
159
+ for i in range(6):
160
+ payload[i] = 0xFF
161
+ for i in range(6, len(payload), 6):
162
+ payload[i:] = mac
163
+
164
+ sock.sendto(payload, addr)
165
+
166
+ def loadConfig():
167
+ try:
168
+ with open(CONFIG_PATH, 'rt') as config:
169
+ config = json.load(config)
170
+ if type(config) != dict:
171
+ raise WakeOnLanError(f'{CONFIG_PATH} is malformed')
172
+ return config
173
+ except json.JSONDecodeError:
174
+ raise WakeOnLanError(f'{CONFIG_PATH} is malformed')
175
+ except OSError:
176
+ pass
177
+ return {'names':{}}
178
+
179
+ def saveConfig(config):
180
+ try:
181
+ with open(CONFIG_TMP_PATH, 'wt') as tempfile:
182
+ json.dump(config, tempfile, indent=2)
183
+ shutil.move(CONFIG_TMP_PATH, CONFIG_PATH)
184
+ except OSError as err:
185
+ raise WakeOnLanError(f'Unable to save: {err.strerror}')
186
+
187
+ def getNamesDict(config):
188
+ names = config.get('names')
189
+ if type(names) != dict:
190
+ raise WakeOnLanError(f'`names` not found in {CONFIG_PATH}')
191
+ return names
192
+
193
+ def parseNameRecord(name, nameRecord):
194
+ if type(nameRecord) != dict:
195
+ raise WakeOnLanError(f'`{name}` entry in {CONFIG_PATH} is malformed')
196
+ mac = nameRecord.get('mac')
197
+ if type(mac) != str or not MAC_PATTERN.match(mac):
198
+ raise WakeOnLanError(f'mac address in `{name}` entry in {CONFIG_PATH} is missing or malformed')
199
+ mac = splitMac(mac)
200
+ ip = nameRecord.get('ip', DEFAULT_IP)
201
+ if type(ip) != str or not IP_PATTERN.match(ip):
202
+ raise WakeOnLanError(f'ip address in `{name}` entry in {CONFIG_PATH} is malformed')
203
+ port = nameRecord.get('port', DEFAULT_PORT)
204
+ if type(port) != int or port < 0 or port > 65535:
205
+ raise WakeOnLanError(f'port address in `{name}` entry in {CONFIG_PATH} is malformed')
206
+
207
+ return (mac, (ip, port))
208
+
209
+ def getNameRecord(name: str) -> Union[HostRecord, None]:
210
+ config = loadConfig()
211
+ names = getNamesDict(config)
212
+ nameRecord = names.get(name)
213
+ if nameRecord is None:
214
+ return None
215
+ return parseNameRecord(name, nameRecord)
216
+
217
+ def getNames() -> Dict[str, HostRecord] :
218
+ config = loadConfig()
219
+ names = getNamesDict(config)
220
+ ret = {}
221
+ for name, nameRecord in names.items():
222
+ ret[name] = parseNameRecord(name, nameRecord)
223
+ return ret
224
+
225
+
226
+ def saveName(name: str, mac: MacAddress, ipaddr: IPAddress, port: Port) -> None :
227
+ config = loadConfig()
228
+ names = getNamesDict(config)
229
+ record: Dict[str, Any] = {
230
+ 'mac': joinMac(mac)
231
+ }
232
+ if ipaddr != DEFAULT_IP:
233
+ record['ip'] = ipaddr
234
+ if port != DEFAULT_PORT:
235
+ record['port'] = port
236
+ names[name] = record
237
+ saveConfig(config)
238
+
239
+ def deleteName(name: str) -> None :
240
+ config = loadConfig()
241
+ names = getNamesDict(config)
242
+ names.pop(name, None)
243
+ saveConfig(config)
244
+
245
+ def main():
246
+ args = parseArgs()
247
+
248
+ try:
249
+
250
+ if args.cmd == WAKE_CMD:
251
+ print(f'wake: {args.macOrName}, {args.ipaddr}, {args.port}')
252
+ wake(args.macOrName, (args.ipaddr, args.port))
253
+ elif args.cmd == WAKE_BY_NAME_CMD:
254
+ nameRecord = getNameRecord(args.macOrName)
255
+ if nameRecord is None:
256
+ raise WakeOnLanError(f'Name {args.macOrName} not found')
257
+ mac, addr = nameRecord
258
+ print(f'wake: {joinMac(mac)}, {addr[0]}, {addr[1]}')
259
+ wake(mac, addr)
260
+ elif args.cmd == SAVE_CMD:
261
+ saveName(args.saveName, args.macOrName, args.ipaddr, args.port)
262
+ print(f'Name {args.saveName} saved')
263
+ elif args.cmd == DELETE_CMD:
264
+ deleteName(args.deleteName)
265
+ print(f'Name {args.deleteName} deleted')
266
+ elif args.cmd == LIST_CMD:
267
+ names = getNames()
268
+ for name, nameRecord in names.items():
269
+ mac, addr = nameRecord
270
+ mac = joinMac(mac)
271
+ print(f'{name} - {mac}, {addr[0]}, {addr[1]}')
272
+ return 0
273
+ except WakeOnLanError as ex:
274
+ print(ex, file=sys.stderr)
275
+ return 1
@@ -0,0 +1,17 @@
1
+ from wakeonlan import *
2
+
3
+ def test_save():
4
+ assert getNames() == {}
5
+ saveName("test", (1,1,1,1,1,1), "127.0.0.1", 9)
6
+ rec = getNameRecord("test")
7
+ assert not rec is None
8
+ assert len(rec) == 2
9
+ assert rec[0] == [1,1,1,1,1,1]
10
+ assert rec[1] == ("127.0.0.1", 9)
11
+ assert getNames() == {
12
+ "test": ([1,1,1,1,1,1], ("127.0.0.1", 9))
13
+ }
14
+ deleteName("test")
15
+ assert getNames() == {}
16
+ assert getNameRecord("test") == None
17
+