#!/usr/bin/env python# -*- coding: utf-8 -*-## @Author: José Sánchez-Gallego (gallegoj@uw.edu)# @Date: 2023-11-23# @Filename: netio.py# @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)from__future__importannotationsimportwarningsfromtypingimportAnyimporthttpxfrompydanticimportConfigDict,SecretStrfrompydantic.dataclassesimportdataclassfromlvmnpsimportlogfromlvmnps.exceptionsimportNPSWarning,VerificationErrorfromlvmnps.nps.coreimportNPSClient,OutletModelfromlvmnps.toolsimportAPIClientclassNetIOOutLetModel(OutletModel):"""Outlet model for NetIO switches."""pass
[docs]@dataclass(config=ConfigDict(extra="forbid"))classNetIOClient(NPSClient):"""An NPS client for a NetIO switch. This implementation uses the JSON API, see https://www.netio-products.com/files/NETIO-M2M-API-Protocol-JSON.pdf """host:strport:int=80user:str="admin"password:SecretStr=SecretStr("admin")def__post_init__(self):super().__init__()self.base_url=f"http://{self.host}:{self.port}"self.api_client=APIClient(self.base_url,self.user,self.password,auth_method="basic",)self.outlets:dict[str,NetIOOutLetModel]={}self.nps_type="netio"self.implementations={"scripting":False}
[docs]asyncdefsetup(self):"""Sets up the power supply, setting any required configuration options."""log.info("Setting up NetIO switch.")try:awaitself.verify()exceptVerificationErroraserr:warnings.warn("Cannot setup NetIO. Power switch "f"verification failed with error: {err}",NPSWarning,)returnawaitself.refresh()log.info("Set up complete.")
[docs]asyncdefverify(self):"""Checks that the NPS is connected and responding."""asyncwithself.api_clientasclient:try:response=awaitclient.get(url="/netio.json")excepthttpx.ConnectErroraserr:raiseVerificationError(f"Failed to connect to NetIO: {err}")self._validate_response(response,200)
[docs]asyncdefrefresh(self):"""Refreshes the list of outlets."""log.debug("Refreshing list of outlets.")asyncwithself.api_clientasclient:response=awaitclient.get(url="/netio.json")self._validate_response(response)data=response.json()outlet_json=data["Outputs"]log.debug(f"Found {len(outlet_json)} outlets.")self.outlets={}fordatainoutlet_json:outlet=NetIOOutLetModel(id=data["ID"],name=data["Name"],state=data["State"],)outlet.set_client(self)self.outlets[outlet.normalised_name]=outlet
asyncdef_set_state_internal(self,outlets:list[NetIOOutLetModel],on:bool=False,off_after:float|None=None,):"""Sets the state of a list of outlets."""outputs:list[dict[str,Any]]=[]foroutletinoutlets:outlet_action:dict[str,Any]={"ID":outlet.id,"Action":int(on)}ifonisTrueandoff_afterisnotNone:outlet_action["Action"]=3outlet_action["Delay"]=off_after*1000outputs.append(outlet_action)asyncwithself.api_clientasclient:response=awaitclient.post(url="/netio.json",json={"Outputs":outputs},)self._validate_response(response)return