Coverage for jumpstarter_driver_energenie/driver.py: 73%

55 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-06 10:21 +0200

1 

2import requests 

3from collections.abc import AsyncGenerator 

4from dataclasses import dataclass, field 

5 

6from jumpstarter_driver_power.driver import PowerInterface, PowerReading 

7 

8from jumpstarter.driver import Driver, export 

9 

10 

11@dataclass(kw_only=True) 

12class EnerGenie(PowerInterface, Driver): 

13 """ 

14 driver for the EnerGenie Programmable surge protector with LAN interface. 

15 

16 This driver was tested on EG-PMS2-LAN device only but should be easy to support other devices. 

17 """ 

18 

19 host: str | None = field(default=None) 

20 password: str | None = field(default="1") 

21 slot: int = 1 

22 

23 def login(self): 

24 """ 

25 Log in to the programmable power switch. 

26 

27 :return: True if login is successful, False otherwise. 

28 """ 

29 login_url = f"{self.base_url}/login.html" 

30 try: 

31 response = requests.post(login_url, data={"pw": self.password}, timeout=10) 

32 return response.status_code == 200 

33 except (requests.exceptions.ConnectionError, requests.exceptions.Timeout, 

34 requests.exceptions.RequestException) as e: 

35 self.logger.error(f"Login failed: {str(e)}") 

36 return False 

37 

38 def __post_init__(self): 

39 if hasattr(super(), "__post_init__"): 

40 super().__post_init__() 

41 # Programmable power switch initialitzation. The EG-PMS2-LAN device has up to 4 slots. 

42 if self.slot < 1 or self.slot > 4: 

43 raise ValueError("Slot must be between 1 and 4") 

44 if self.host is None: 

45 raise ValueError("Host must be specified") 

46 self.logger.debug(f"Using Host: {self.host}, Slot: {self.slot}") 

47 self.base_url = f"http://{self.host}" 

48 

49 

50 def set_switch(self, switch_number, state): 

51 """ 

52 Set the state of a specific switch. 

53 

54 :param switch_number: The switch number (1, 2, etc.). 

55 :param state: The state to set (1 for ON, 0 for OFF). 

56 :return: True if the operation is successful, False otherwise. 

57 """ 

58 if state not in [0, 1]: 

59 self.logger.error(f"Invalid state: {state}") 

60 return False 

61 

62 if self.login(): 

63 self.logger.debug("Login successful!") 

64 else: 

65 self.logger.debug("Login failed!") 

66 return False 

67 data = {f"cte{switch_number}": state} 

68 try: 

69 response = requests.post(self.base_url, data=data, timeout=10) 

70 if response.status_code != 200: 

71 self.logger.error(f"Set switch {switch_number} to {state} state failed!") 

72 return False 

73 except (requests.exceptions.ConnectionError, requests.exceptions.Timeout, 

74 requests.exceptions.RequestException) as e: 

75 self.logger.error(f"Set switch failed: {str(e)}") 

76 return False 

77 

78 self.logger.debug(f"Set switch {switch_number} to {state} state") 

79 

80 return True 

81 

82 @export 

83 def on(self) -> None: 

84 self.set_switch(self.slot, 1) 

85 

86 @export 

87 def off(self) -> None: 

88 self.set_switch(self.slot, 0) 

89 

90 @export 

91 def read(self) -> AsyncGenerator[PowerReading, None]: 

92 raise NotImplementedError