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