power: use a mutex to prevent concurrent device requests

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Arksine 2021-05-10 07:25:56 -04:00
parent 724c29c4a7
commit 0917536ccc
1 changed files with 48 additions and 42 deletions

View File

@ -13,7 +13,7 @@ import socket
import gpiod
from tornado.ioloop import IOLoop
from tornado.iostream import IOStream
from tornado import gen
from tornado.locks import Lock
from tornado.httpclient import AsyncHTTPClient
from tornado.escape import json_decode
@ -238,6 +238,7 @@ class HTTPDevice(PowerDevice):
default_user=None, default_password=None):
super().__init__(config)
self.client = AsyncHTTPClient()
self.request_mutex = Lock()
self.addr = config.get("address")
self.port = config.getint("port", default_port)
self.user = config.get("user", default_user)
@ -265,24 +266,26 @@ class HTTPDevice(PowerDevice):
"_send_status_request must be implemented by children")
async def refresh_status(self):
try:
state = await self._send_status_request()
except Exception:
self.state = "error"
msg = f"Error Refeshing Device Status: {self.name}"
logging.exception(msg)
raise self.server.error(msg) from None
self.state = state
async with self.request_mutex:
try:
state = await self._send_status_request()
except Exception:
self.state = "error"
msg = f"Error Refeshing Device Status: {self.name}"
logging.exception(msg)
raise self.server.error(msg) from None
self.state = state
async def set_power(self, state):
try:
state = await self._send_power_request(state)
except Exception:
self.state = "error"
msg = f"Error Setting Device Status: {self.name} to {state}"
logging.exception(msg)
raise self.server.error(msg) from None
self.state = state
async with self.request_mutex:
try:
state = await self._send_power_request(state)
except Exception:
self.state = "error"
msg = f"Error Setting Device Status: {self.name} to {state}"
logging.exception(msg)
raise self.server.error(msg) from None
self.state = state
class GpioChipFactory:
@ -381,6 +384,7 @@ class TPLinkSmartPlug(PowerDevice):
START_KEY = 0xAB
def __init__(self, config):
super().__init__(config)
self.request_mutex = Lock()
self.addr = config.get("address").split('/')
self.port = config.getint("port", 9999)
@ -448,33 +452,35 @@ class TPLinkSmartPlug(PowerDevice):
await self.refresh_status()
async def refresh_status(self):
try:
res = await self._send_tplink_command("info")
if len(self.addr) == 2:
# TPLink device controls multiple devices
children = res['system']['get_sysinfo']['children']
state = children[int(self.addr[1])]['state']
else:
state = res['system']['get_sysinfo']['relay_state']
except Exception:
self.state = "error"
msg = f"Error Refeshing Device Status: {self.name}"
logging.exception(msg)
raise self.server.error(msg) from None
self.state = "on" if state else "off"
async with self.request_mutex:
try:
res = await self._send_tplink_command("info")
if len(self.addr) == 2:
# TPLink device controls multiple devices
children = res['system']['get_sysinfo']['children']
state = children[int(self.addr[1])]['state']
else:
state = res['system']['get_sysinfo']['relay_state']
except Exception:
self.state = "error"
msg = f"Error Refeshing Device Status: {self.name}"
logging.exception(msg)
raise self.server.error(msg) from None
self.state = "on" if state else "off"
async def set_power(self, state):
try:
res = await self._send_tplink_command(state)
err = res['system']['set_relay_state']['err_code']
except Exception:
err = 1
logging.exception(f"Power Toggle Error: {self.name}")
if err:
self.state = "error"
raise self.server.error(
f"Error Toggling Device Power: {self.name}")
self.state = state
async with self.request_mutex:
try:
res = await self._send_tplink_command(state)
err = res['system']['set_relay_state']['err_code']
except Exception:
err = 1
logging.exception(f"Power Toggle Error: {self.name}")
if err:
self.state = "error"
raise self.server.error(
f"Error Toggling Device Power: {self.name}")
self.state = state
class Tasmota(HTTPDevice):