Source code for lvmnps.switch.dli.lvmpower

# !usr/bin/env python
# -*- coding: utf-8 -*-
#
# Licensed under a 3-clause BSD license.
#
# @Author: Mingyeong Yang (mingyeong@khu.ac.kr)
# @Date: 2021-08-12


from __future__ import annotations

import httpx
from bs4 import BeautifulSoup


CONFIG_DEFAULTS = {
    "userid": "admin",
    "password": "1234",
    "hostname": "192.168.0.100",
    "port": "80",
    "name": "switch",
}


[docs]class PowerSwitch(object): def __init__( self, userid=None, password=None, hostname=None, name=None, port=None, use_https=False, ): """ Class initialization """ config = self.load_configuration() if userid: self.userid = userid else: self.userid = config["userid"] if password: self.password = password else: self.password = config["password"] if hostname: self.hostname = hostname else: self.hostname = config["hostname"] if port: self.port = port else: self.port = config["port"] if name: self.name = name else: self.name = config["name"] self.scheme = "http" self.base_url = "%s://%s" % (self.scheme, self.hostname) self.clients = {}
[docs] def load_configuration(self): """Return a configuration dictionary""" return CONFIG_DEFAULTS
[docs] async def add_client(self): """Access the url object""" auth = httpx.DigestAuth(self.userid, self.password) self.clients[self.hostname] = httpx.AsyncClient( auth=auth, base_url=self.base_url, headers={}, )
[docs] async def login(self): """Access plaintext URL logins""" login_url = "%s/login.tgi" % self.base_url data = {"Username": self.userid, "Password": self.password} headers = { "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0)\ Gecko/20100101 Firefox/89.0", "Content-Type": "application/x-www-form-urlencoded", } try: res = await self.clients[self.hostname].post( url=login_url, data=data, headers=headers, ) except httpx.RequestError as exc: print(f"An error {exc} occurred while requesting {login_url!r}.") if res.status_code != 200: raise Exception( f"Error response {res.status_code} while requesting {login_url}." )
[docs] async def close(self): """Close the Connection with URL""" await self.clients[self.hostname].aclose()
[docs] async def verify(self): """Verify we can reach the switch, returns true if ok""" if await self.geturl(): return True return False
[docs] async def geturl(self, url="index.htm"): """Get a URL""" await self.add_client() await self.login() full_url = "%s/%s" % (self.base_url, url) if self.hostname not in self.clients: raise ValueError(f"Client for hostname {self.hostname} not defined.") res = await self.clients[self.hostname].get(url=full_url) if res.status_code != 200: raise RuntimeError(f"GET returned code {res.status_code}.") else: result = res.content await self.close() return result
[docs] async def on(self, outlet_number=0): """Turn on power to an outlet""" await self.geturl(url="outlet?%d=ON" % outlet_number)
[docs] async def onall(self): """Turn on all outlets""" await self.geturl(url="outlet?%s=ON" % "a")
[docs] async def off(self, outlet_number=0): """Turn off power to an outlet""" await self.geturl(url="outlet?%d=OFF" % outlet_number)
[docs] async def cycle(self, outlet_number: int): """cycle power to an outlet""" await self.geturl(url="outlet?%d=CCL" % outlet_number)
[docs] async def statuslist(self): """Return the status of all outlets in a list, each item will contain 3 items plugnumber, hostname and state""" outlets = [] url = await self.geturl("index.htm") if not url: return None soup = BeautifulSoup(url, "html.parser") # Get the root of the table containing the port status info try: root = soup.findAll("td", text="1")[0].parent.parent.parent except IndexError: # Finding the root of the table with the outlet info failed # try again assuming we're seeing the table for a user # account insteaed of the admin account (tables are different) try: self._is_admin = False root = soup.findAll("th", text="#")[0].parent.parent.parent except IndexError: return None for temp in root.findAll("tr"): columns = temp.findAll("td") if len(columns) == 5: plugnumber = columns[0].string name = columns[1].string state = columns[2].find("font").string.upper() outlets.append([int(plugnumber), name, state]) return outlets
[docs] async def statusdictionary(self): """Return the status of all outlets in a dictionary, each item will contain 2 items plugnumber, status""" outlets = await self.statuslist() outlets_dict = {} num = range(0, 8) for n in num: plugnumber = outlets[n][0] outlets_dict[plugnumber] = outlets[n][2] return outlets_dict
[docs] async def printstatus(self): """Print the status off all the outlets as a table to stdout""" if not await self.statuslist(): print("Unable to communicate to the Web power switch at %s" % self.hostname) return None print("Outlet\t%-15.15s\tState" % "Name") for item in await self.statuslist(): print("%d\t%-15.15s\t%s" % (item[0], item[1], item[2])) return