TP-Link-Archer-C6U
TP-Link-Archer-C6U copied to clipboard
Added the ability to toggle the LEDs on the router, and made it possible to read the current state of the LEDs
It is now possible to get the current status of the LEDs with
led = status.led
And set the LEDs with
router.set_led(True)
This code works for the TP-Link archer C1200 V2 I am positive it will work on other routers.
Hi. It is breaking compatibility. It is better to add 2 methods get_led and set_led to TplinkC1200Router
Thank you for your reply! I hope I fixed the compatibility issue correctly now.
@swwgames Hi One more question Is it would be enough?
def get_led(self) -> bool:
What do you think?
I think you are right. I will fix that real quick.
@swwgames Could you add tests for new methods to test_client.py?
Ok, I will try to do that.
Do I need to place the new test under the TPLinkRouterTest class, or do I need to create a new class or function specifically for the Archer C1200?
Yes, I think it would be better to create a new file test_client_c1200.py and ceate a new class class TestTPLinkC1200Client(unittest.TestCase):
Right now I have the tests for the led_status and wifi_status functions. Do I need to add all the other tests or is this enough?
This is my code so far:
import unittest
import json
import macaddress
import ipaddress
from tplinkrouterc6u import (
TplinkC1200Router,
Connection,
ClientException
)
class TestTPLinkC1200Client(unittest.TestCase):
def test_led_status(self) -> None:
response_led_general_read = '''
{
"enable": "on",
"time_set": "yes",
"ledpm_support": "yes"
}
'''
response_led_general_write = '''
{
"enable": "off",
"time_set": "yes",
"ledpm_support": "yes"
}
'''
class TPLinkRouterTest(TplinkC1200Router):
def request(self, path: str, data: str,
ignore_response: bool = False, ignore_errors: bool = False) -> dict | None:
if path == 'admin/ledgeneral?form=setting&operation=read':
return json.loads(response_led_general_read)
elif path == 'admin/ledgeneral?form=setting&operation=write':
self.captured_path = path
return json.loads(response_led_general_write)
raise ClientException()
client = TPLinkRouterTest('', '')
led_status = client.get_led()
self.assertTrue(led_status)
client.set_led(False)
expected_path = "admin/ledgeneral?form=setting&operation=write"
self.assertEqual(client.captured_path, expected_path)
class TestTPLinkClient(unittest.TestCase):
def test_set_wifi(self) -> None:
class TPLinkRouterTest(TplinkC1200Router):
def request(self, path: str, data: str,
ignore_response: bool = False, ignore_errors: bool = False) -> dict | None:
self.captured_path = path
self.captured_data = data
client = TPLinkRouterTest('', '')
client.set_wifi(
Connection.HOST_5G,
enable=True,
ssid="TestSSID",
hidden="no",
encryption="WPA3-PSK",
psk_version="2",
psk_cipher="AES",
psk_key="testkey123",
hwmode="11ac",
htmode="VHT20",
channel=36,
txpower="20",
disabled_all="no"
)
expected_data = ("operation=write&enable=on&ssid=TestSSID&hidden=no&encryption=WPA3-PSK&"
"psk_version=2&psk_cipher=AES&psk_key=testkey123&hwmode=11ac&"
"htmode=VHT20&channel=36&txpower=20&disabled_all=no")
expected_path = f"admin/wireless?form=wireless_5g&{expected_data}"
self.assertEqual(client.captured_path, expected_path)
self.assertEqual(client.captured_data, expected_data)
if __name__ == '__main__':
unittest.main()
You dont need to declare
class TestTPLinkClient(unittest.TestCase):
And it would be great if you write tests
- def test_set_led_on(self)
- def test_set_led_off(self)
- def test_get_led(self)
in this MR you dont need test_set_wifi as here is no method for that
So, is this correct?
import unittest
import json
from tplinkrouterc6u import (
TplinkC1200Router,
ClientException
)
class TestTPLinkC1200Client(unittest.TestCase):
def test_set_led_on(self) -> None:
response_led_general_read = '''
{
"enable": "off",
"time_set": "yes",
"ledpm_support": "yes"
}
'''
response_led_general_write = '''
{
"enable": "on",
"time_set": "yes",
"ledpm_support": "yes"
}
'''
class TPLinkRouterTest(TplinkC1200Router):
def request(self, path: str, data: str,
ignore_response: bool = False, ignore_errors: bool = False) -> dict | None:
if path == 'admin/ledgeneral?form=setting&operation=read':
return json.loads(response_led_general_read)
elif path == 'admin/ledgeneral?form=setting&operation=write':
self.captured_path = path
return json.loads(response_led_general_write)
raise ClientException()
client = TPLinkRouterTest('', '')
client.set_led(True)
expected_path = "admin/ledgeneral?form=setting&operation=write"
self.assertEqual(client.captured_path, expected_path)
def test_set_led_off(self) -> None:
response_led_general_read = '''
{
"enable": "on",
"time_set": "yes",
"ledpm_support": "yes"
}
'''
response_led_general_write = '''
{
"enable": "off",
"time_set": "yes",
"ledpm_support": "yes"
}
'''
class TPLinkRouterTest(TplinkC1200Router):
def request(self, path: str, data: str,
ignore_response: bool = False, ignore_errors: bool = False) -> dict | None:
if path == 'admin/ledgeneral?form=setting&operation=read':
return json.loads(response_led_general_read)
elif path == 'admin/ledgeneral?form=setting&operation=write':
self.captured_path = path
return json.loads(response_led_general_write)
raise ClientException()
client = TPLinkRouterTest('', '')
client.set_led(False)
expected_path = "admin/ledgeneral?form=setting&operation=write"
self.assertEqual(client.captured_path, expected_path)
def test_led_status(self) -> None:
response_led_general_read = '''
{
"enable": "on",
"time_set": "yes",
"ledpm_support": "yes"
}
'''
class TPLinkRouterTest(TplinkC1200Router):
def request(self, path: str, data: str,
ignore_response: bool = False, ignore_errors: bool = False) -> dict | None:
if path == 'admin/ledgeneral?form=setting&operation=read':
return json.loads(response_led_general_read)
raise ClientException()
client = TPLinkRouterTest('', '')
led_status = client.get_led()
self.assertTrue(led_status)
if __name__ == '__main__':
unittest.main()
from test_set_led_on and test_set_led_off delete please response_led_general_read and this block
if path == 'admin/ledgeneral?form=setting&operation=read':
return json.loads(response_led_general_read)
``
That part is needed, because it is used in the set_led function.
def set_led(self, enable: bool) -> None:
current_state = self.request('admin/ledgeneral?form=setting&operation=read', 'operation=read').get('enable', 'off') == 'on'
if current_state != enable:
self.request('admin/ledgeneral?form=setting&operation=write', 'operation=write')
The code checks the current state, if the state is not in the desired state, a request is made to toggle the state. You can't specify to which state it needs to change.
So setting ON or OFF can be done just by one request self.request('admin/ledgeneral?form=setting&operation=write', 'operation=write') ? it works like switch when you request self.request('admin/ledgeneral?form=setting&operation=write', 'operation=write') it switches to apposite value?
Yes, self.request('admin/ledgeneral?form=setting&operation=write', 'operation=write') toggles the state. I wanted to be able to specify to which state the led needs to change. Because of that there also needs to be a request made to self.request('admin/ledgeneral?form=setting&operation=read', 'operation=read') so that the function can check if it needs to send a request to self.request('admin/ledgeneral?form=setting&operation=write', 'operation=write').
I hope this clarifies how this function works.
Do you think the test script is correct right now? If so, I can add it to my fork and we will be able to merge.
Thank you for the clarification
I have no more question for test script. Lets add it
I added the test file to my fork. I think the fork is ready to merge.
@swwgames Thank you!