Sensors and reboot switch for Arcadyan VGV7519 modem (Experia Box v8)

I’ve created a set of sensors switches for my Arcadyan modem that is provided by my internet provider. The sensors provide more information on the current connection speeds, the switches can be used to switch on/of the wireless connection or to reboot the modem.

I wouldn’t know where to start to create a HA component out of this, but I am happy to share the python scripts + configuration in HA, see below. Please note I am not a real programmer, the code might be ugly and not very configurable, but it works :wink:

Replace <PASSWORD> <YOUR_SSID> and <MODEM_IP> with your own settings. My scripts are located in the /home/pi/ folder.

configuration.yaml

sensor 11:
  platform: command_line
  name: getmodemdata
  command: "/home/pi/get-modem-info.py"
  
sensor 12:
  platform: command_line
  name: getphonelogdata
  command: "/home/pi/get-modem-phonelog.py"

sensor 13:
  platform: template
  sensors:
    modem_dsl_mode:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[0] }}"
     friendly_name: 'DLS-mode'
    modem_vdsl_status:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[1] }}"
     friendly_name: 'Status'
    modem_wan_ip:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[2] }}"
     friendly_name: 'Public IP'
    modem_primary_dns:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[3] }}"
     friendly_name: 'Primary DNS'
    modem_secondary_dns:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[4] }}"
     friendly_name: 'Secondary DNS'
    modem_download_rate:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[5] }}"
     friendly_name: 'Download rate'
     unit_of_measurement: "Kbps"
    modem_upload_rate:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[6] }}"
     friendly_name: 'Upload rate'
     unit_of_measurement: "Kbps"
    modem_attainable_download_rate:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[7] }}"
     friendly_name: 'Attainable download rate'
     unit_of_measurement: "Kbps"
    modem_attainable_upload_rate:
     value_template: "{% set list = states.sensor.getmodemdata.state.split(';') %} {{ list[8] }}"
     friendly_name: 'Attainable upload rate'
     unit_of_measurement: "Kbps"
    phone_incoming_from:
     value_template: "{% set list = states.sensor.getphonelogdata.state.split(';') %} {{ list[0] }}"
     friendly_name: 'Phonenumber caller'
    phone_incoming_to:
     value_template: "{% set list = states.sensor.getphonelogdata.state.split(';') %} {{ list[1] }}"
     friendly_name: 'My phonenumber'
    phone_incoming_date:
     value_template: "{% set list = states.sensor.getphonelogdata.state.split(';') %} {{ list[2] }}"
     friendly_name: 'Date and time'
  
switch 1:
  platform: command_line
  switches:
    herstart_modem:
      command_on: "/home/pi/set-modem-reboot.py"
      friendly_name: Reboot modem
    modem_wireless:
      command_on: "/home/pi/set-modem-wireless.py 1"
      command_off: "/home/pi/set-modem-wireless.py 0"
      command_state: "/home/pi/get-modem-wireless.py" 
      value_template: '{{ value == "1" }}'
      friendly_name: WiFi guest

get-modem-info.py

    #!/usr/bin/python3
    import sys
    import requests
    from lxml import html

    session_requests = requests.session()

    login_url_initial = "http://<MODEM_IP>/login.stm"
    result = session_requests.get(login_url_initial)

    tree = html.fromstring(result.text)
    authenticity_token = tree.xpath('//script[contains(.,"httoken")]/text()')[0]
    authenticity_token = authenticity_token.split("httoken = '",1)[1]
    authenticity_token = authenticity_token.split("'",1)[0] 


    payload = {
    	"user": "Admin", 
    	"pws": "<PASSWORD>", 
    	"httoken": authenticity_token
    }

    login_url_second = "http://<MODEM_IP>/cgi-bin/login.exe"

    result = session_requests.post(
    	login_url_second, 
    	data = payload, 
    	headers = dict(referer=login_url_initial)
    )

    data_url = "http://<MODEM_IP>/status_main.stm"
    result = session_requests.get(data_url)
    tree = html.fromstring(result.text)

    scriptdata = tree.xpath('//script[contains(.,"bWanConnected")]/text()')[0]

    dsl_mode = scriptdata.split("var dsl_mode=",1)[1].split(";",1)[0]

    if dsl_mode == "1":
    	dsl_mode = "VDSL"
    else:
    	dsl_mode = "ADSL"
         
    vdsl_status = scriptdata.split("var bWanConnected=",1)[1].split(";",1)[0]

    if vdsl_status == "1":
    	vdsl_status = "Connected"
    else:
    	vdsl_status = "Disconnected"


    wan_ip = scriptdata.split('var wan_ip="',1)[1].split('";',1)[0]
    primary_dns = scriptdata.split('var primary_dns="',1)[1].split('";',1)[0]
    secondary_dns = scriptdata.split('var secondary_dns="',1)[1].split('";',1)[0]
    download_rate = scriptdata.split("var download_rate=",1)[1].split(";",1)[0]
    upload_rate = scriptdata.split("var upload_rate=",1)[1].split(";",1)[0]

    data_url = "http://<MODEM_IP>/adsl_status.stm"
    result = session_requests.get(data_url)
    tree = html.fromstring(result.text)

    scriptdata = tree.xpath('//table[1]/tr[1]/td[1]/table[2]/tr[3]/td[2]/text()')[0]
    attainable_upload_rate = scriptdata.split(" ",1)[0]

    scriptdata = tree.xpath('//table[1]/tr[1]/td[1]/table[2]/tr[3]/td[3]/text()')[0]
    attainable_download_rate = scriptdata.split(" ",1)[0]

    logout_url = "http://<MODEM_IP>/cgi-bin/logout.exe?_tn=" + authenticity_token
    result = session_requests.get(logout_url)

    print (str(dsl_mode)+";"+ str(vdsl_status)+ ";"+ str(wan_ip)+ ";"+ str(primary_dns)+ ";"+ str(secondary_dns)+ ";"+ str(download_rate)+ ";"+ str(upload_rate) + ";"+str(attainable_download_rate) + ";" + str(attainable_upload_rate))

get-modem-phonelog.py

    #!/usr/bin/python3
    import sys
    import requests
    from lxml import html

    session_requests = requests.session()

    login_url_initial = "http://<MODEM_IP>/login.stm"
    result = session_requests.get(login_url_initial)

    tree = html.fromstring(result.text)
    authenticity_token = tree.xpath('//script[contains(.,"httoken")]/text()')[0]
    authenticity_token = authenticity_token.split("httoken = '",1)[1]
    authenticity_token = authenticity_token.split("'",1)[0] 


    payload = {
    	"user": "Admin", 
    	"pws": "<PASSWORD>", 
    	"httoken": authenticity_token
    }

    login_url_second = "http://<MODEM_IP>/cgi-bin/login.exe"

    result = session_requests.post(
    	login_url_second, 
    	data = payload, 
    	headers = dict(referer=login_url_initial)
    )

    data_url = "http://<MODEM_IP>/voip_status.stm"
    result = session_requests.get(data_url)
    tree = html.fromstring(result.text)

    try:
        scriptdata = tree.xpath('//script[contains(.,"var i_count=")]/text()')[0]
        incoming_count = int(scriptdata.split("var i_count= ",1)[1].split(";",1)[0])
        phone_incoming_from = scriptdata.split("voip_i_Phone["+str(incoming_count-1)+"] = '",1)[1].split("';",1)[0]
        phone_incoming_to = scriptdata.split("voip_i_AC_Phone["+str(incoming_count-1)+"] = '",1)[1].split("';",1)[0]
        phone_incoming_date = scriptdata.split("voip_i_Date["+str(incoming_count-1)+"] = '",1)[1].split("';",1)[0]
        print (str(phone_incoming_from)+";"+ str(phone_incoming_to)+ ";"+ str(phone_incoming_date))
    except:
        print(" ; ; ")

    logout_url = "http://<MODEM_IP>/cgi-bin/logout.exe?_tn=" + authenticity_token
    result = session_requests.get(logout_url)

get-modem-wireless.py

    #!/usr/bin/python3
    import sys
    import requests
    from lxml import html

    session_requests = requests.session()

    login_url_initial = "http://<MODEM_IP>/login.stm"
    result = session_requests.get(login_url_initial)

    tree = html.fromstring(result.text)
    authenticity_token = tree.xpath('//script[contains(.,"httoken")]/text()')[0]
    authenticity_token = authenticity_token.split("httoken = '",1)[1]
    authenticity_token = authenticity_token.split("'",1)[0] 


    payload = {
    	"user": "Admin", 
    	"pws": "<PASSWORD>", 
    	"httoken": authenticity_token
    }

    login_url_second = "http://<MODEM_IP>/cgi-bin/login.exe"

    result = session_requests.post(
    	login_url_second, 
    	data = payload, 
    	headers = dict(referer=login_url_initial)
    )

    data_url = "http://<MODEM_IP>/wireless_main.stm"
    result = session_requests.get(data_url)
    tree = html.fromstring(result.text)

    scriptdata = tree.xpath('//script[contains(.,"var iDOD = 1 -")]/text()')[0]

    wireless_status = scriptdata.split("var iDOD = 1 - ",1)[1].split(";",1)[0]

    if wireless_status == "0":
    	wireless_status = "0"
    else:
    	wireless_status = "1"

    logout_url = "http://<MODEM_IP>/cgi-bin/logout.exe?_tn=" + authenticity_token
    result = session_requests.get(logout_url)

    print (str(wireless_status))

set-modem-reboot.py

    #!/usr/bin/python3
    import sys
    import requests
    from lxml import html

    session_requests = requests.session()

    login_url_initial = "http://<MODEM_IP>/login.stm"
    result = session_requests.get(login_url_initial)

    tree = html.fromstring(result.text)
    authenticity_token = tree.xpath('//script[contains(.,"httoken")]/text()')[0]
    authenticity_token = authenticity_token.split("httoken = '",1)[1]
    authenticity_token = authenticity_token.split("'",1)[0] 


    payload = {
    	"user": "Admin", 
    	"pws": "<PASSWORD>", 
    	"httoken": authenticity_token
    }

    login_url_second = "http://<MODEM_IP>/cgi-bin/login.exe"

    result = session_requests.post(
    	login_url_second, 
    	data = payload, 
    	headers = dict(referer=login_url_initial)
    )

    data_url = "http://<MODEM_IP>/system_r.stm"

    payload = {
            "page": "tools_gateway",
    	"RebootForm": "",
    	"httoken": authenticity_token,
    	"logout": "1"
    }

    reboot_url = "http://<MODEM_IP>/cgi-bin/restart.exe"

    result = session_requests.post(
            reboot_url,
            data = payload,
            headers = dict(referer=data_url)
    )

set-modem-wireless.py

    #!/usr/bin/python3
    import sys
    import requests
    from lxml import html

    session_requests = requests.session()

    login_url_initial = "http://<MODEM_IP>/login.stm"
    result = session_requests.get(login_url_initial)

    tree = html.fromstring(result.text)
    authenticity_token = tree.xpath('//script[contains(.,"httoken")]/text()')[0]
    authenticity_token = authenticity_token.split("httoken = '",1)[1]
    authenticity_token = authenticity_token.split("'",1)[0] 


    payload = {
    	"user": "Admin", 
    	"pws": "<PASSWORD>", 
    	"httoken": authenticity_token
    }

    login_url_second = "http://<MODEM_IP>/cgi-bin/login.exe"

    result = session_requests.post(
    	login_url_second, 
    	data = payload, 
    	headers = dict(referer=login_url_initial)
    )

    data_url = "http://<MODEM_IP>/wireless_main.stm"

    wireless_activated = "0";
    wireless_activated  = sys.argv[1]

    payload = {
            "wlan_ui_ver": "1",
    	"edIdx": "",
    	"confType": "0",
    	"defAct": "",
    	"Wireless_enable": wireless_activated,
    	"button_act": "0",
    	"ssid": "<YOUR_SSID>",
            "httoken": authenticity_token
    }

    wireless_mode_url = "http://<MODEM_IP>/cgi-bin/wireless_eb.exe"

    result = session_requests.post(
            wireless_mode_url,
            data = payload,
            headers = dict(referer=data_url)
    )

    logout_url = "http://<MODEM_IP>/cgi-bin/logout.exe?_tn=" + authenticity_token
    result = session_requests.get(logout_url)

A small addition to code above to create a device tracker for the Arcadyn VGV759 modem. It check on the DHCP log if a given device is connected to the WiFi by MAC Address.

#!/usr/bin/python3
import requests
from lxml import html

session_requests = requests.session()

login_url_initial = "http://<MODEM_IP>/login.stm"
result = session_requests.get(login_url_initial)

tree = html.fromstring(result.text)
authenticity_token = tree.xpath('//script[contains(.,"httoken")]/text()')[0]
authenticity_token = authenticity_token.split("httoken = '",1)[1]
authenticity_token = authenticity_token.split("'",1)[0] 


payload = {
    "user": "Admin", 
    "pws": "<PASSWORD>", 
    "httoken": authenticity_token
}

clear_payload = {
    "securityclear.y": "8",
    "securityclear.x": "57",
    "httoken": authenticity_token
}

login_url_second = "http://<MODEM_IP>/cgi-bin/login.exe"

result = session_requests.post(
    login_url_second, 
    data = payload, 
    headers = dict(referer="http://<MODEM_IP>/status_main.stm")
)

clear_log = session_requests.post(
    "http://<MODEM_IP>/cgi-bin/statusprocess.exe", 
    data = payload, 
    headers = dict(referer=login_url_initial)
)

data_url = "http://<MODEM_IP>/status_main.stm"
result = session_requests.get(data_url)

if "<MAC ADDRESS OF PHONE>" in result.text or "<MAC ADDRESS OF PHONE2>"  in result.text or "<MAC ADDRESS OF PHONE3>"  in result.text: 
    is_home = 1
else:
    is_home = 0

logout_url = "http://<MODEM_IP>/cgi-bin/logout.exe?_tn=" + authenticity_token
result = session_requests.get(logout_url)

print(str(is_home))

Update, created a custom_component for HASS.IO

"""
Support for Arcadyan V7519 routers.
"""
import base64
import hashlib
import logging
import re
from datetime import datetime

import requests
import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import (
    DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    vol.Required(CONF_PASSWORD): cv.string,
    vol.Required(CONF_USERNAME): cv.string
})


def get_scanner(hass, config):
    """Validate the configuration and return a Arcadyan AP scanner."""
    try:
        return ArcadyanDeviceScanner(config[DOMAIN])
    except ConnectionError:
        return None


class ArcadyanDeviceScanner(DeviceScanner):
    """This class queries a wireless router running Arcadyan firmware."""

    def __init__(self, config):
        """Initialize the scanner."""
        host = config[CONF_HOST]
        username, password = config[CONF_USERNAME], config[CONF_PASSWORD]

        self.parse_macs = re.compile('[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}')

        self.host = host
        self.username = username
        self.password = password

        self.last_results = {}
        self.success_init = self._update_info()

    def scan_devices(self):
        """Scan for new devices and return a list with found device IDs."""
        self._update_info()
        return self.last_results

    # pylint: disable=no-self-use
    def get_device_name(self, device):
        """Get firmware doesn't save the name of the wireless device."""
        return None

    def _update_info(self):
        """Ensure the information from the Arcadyan router is up to date.
        Return boolean if scanning successful.
        """
        _LOGGER.info("Loading wireless clients...")

        login_url_initial = 'http://{}/login.stm'.format(self.host)
        page_initial = requests.get(login_url_initial)

        httoken_search = re.search("var _httoken = '(.*)';", page_initial.text)
        authenticity_token = httoken_search.group(1)

        login_payload = {
            "user": self.username, 
            "pws": self.password, 
            "httoken": authenticity_token
        }

        clear_payload = {
            "securityclear.y": "8",
            "securityclear.x": "57",
            "httoken": authenticity_token
        }

        login_url = 'http://{}/cgi-bin/login.exe'.format(self.host)
        start_page = requests.post(login_url, data = login_payload)

        clear_url = 'http://{}/cgi-bin/statusprocess.exe'.format(self.host)
        clear_log = requests.post(clear_url, data = clear_payload)

        data_url = 'http://{}/status_main.stm'.format(self.host)
        data_page = requests.get(data_url)

        result = self.parse_macs.findall(data_page.text)

        logout_url = 'http://{}/cgi-bin/logout.exe?_tn='.format(self.host) + authenticity_token
        log_out_page = requests.get(logout_url)

        if result:
            self.last_results = [mac.replace("-", ":") for mac in result]
            return True

        return False

So awesome, thanks for sharing! I’ve been fidgeting with the Ubiquiti access point that I have connected to my experia box, but with little result. Then I thought just today “what if I let a script ssh into the logs of the experia box…”, but before I dove in I thought let’s check the forums if anybody has done this before. And there’s even a hassio add on!! Will try this ASAP, but at least thanks again for sharing!

Hey @MvdB, a couple of questions :slight_smile: I misread your comment and thought you built an add-on for HASSIO, but you made a custom component. Equally cool, just a little less obvious to install. How do I install this properly? Just save it as a python file in the custom_components and add it to the config file? And where do I set the login credentials?

Hi @michielrutjes , I have made a different topic with explanation how to use. See: Device tracker for Arcadyan VGV7519 router (Experia Box V8)

Good luck and if you have any more questions feel free to ask!