11import logging
2+ import asyncio
23from pymodbus .client import ModbusTcpClient , ModbusUdpClient
34from pymodbus .transaction import ModbusRtuFramer , ModbusIOException
45from pymodbus .pdu import ModbusRequest
56import threading
7+
68try :
79 from homeassistant .const import (
8- DEVICE_CLASS_VOLTAGE ,
10+ DEVICE_CLASS_VOLTAGE ,
911 DEVICE_CLASS_CURRENT ,
1012 DEVICE_CLASS_POWER ,
1113 DEVICE_CLASS_ENERGY ,
1820 DEVICE_CLASS_ENERGY = "energy"
1921 DEVICE_CLASS_POWER_FACTOR = "power_factor"
2022
21- from .const import (
23+ from .const import (
2224 DEVICE_CLASS_FREQUENCY
2325)
2426
2527HPG_SENSOR_TYPES = [
26- DEVICE_CLASS_VOLTAGE ,
28+ DEVICE_CLASS_VOLTAGE ,
2729 DEVICE_CLASS_CURRENT ,
2830 DEVICE_CLASS_POWER ,
29- DEVICE_CLASS_ENERGY ,
31+ DEVICE_CLASS_ENERGY ,
3032 DEVICE_CLASS_POWER_FACTOR ,
3133 DEVICE_CLASS_FREQUENCY
3234]
@@ -52,25 +54,25 @@ class ModbusHub:
5254 def __init__ (self , protocol , host , port , slave ):
5355 self ._lock = threading .Lock ()
5456 self ._slave = slave
55- if ( protocol == "rtuovertcp" ) :
57+ if protocol == "rtuovertcp" :
5658 self ._client = ModbusTcpClient (
57- host = host ,
58- port = port ,
59- framer = ModbusRtuFramer ,
60- timeout = 2 ,
61- retry_on_empty = True ,
62- retry_on_invalid = False
59+ host = host ,
60+ port = port ,
61+ framer = ModbusRtuFramer ,
62+ timeout = 2 ,
63+ retry_on_empty = True ,
64+ retry_on_invalid = False
6365 )
64- elif ( protocol == "rtuoverudp" ) :
66+ elif protocol == "rtuoverudp" :
6567 self ._client = ModbusUdpClient (
66- host = host ,
67- port = port ,
68- framer = ModbusRtuFramer ,
69- timeout = 2 ,
70- retry_on_empty = False ,
71- retry_on_invalid = False
68+ host = host ,
69+ port = port ,
70+ framer = ModbusRtuFramer ,
71+ timeout = 2 ,
72+ retry_on_empty = False ,
73+ retry_on_invalid = False
7274 )
73-
75+
7476 def connect (self ):
7577 with self ._lock :
7678 self ._client .connect ()
@@ -79,24 +81,33 @@ def close(self):
7981 with self ._lock :
8082 self ._client .close ()
8183
82- def read_holding_register (self ):
83- pass
84-
85- def read_input_registers (self , address , count ):
84+ # 新增同步版本,供线程池调用
85+ def read_input_registers_sync (self , address , count ):
8686 with self ._lock :
8787 kwargs = {"slave" : self ._slave }
8888 return self ._client .read_input_registers (address , count , ** kwargs )
8989
90+ # 异步版本,交给线程池执行,避免阻塞事件循环
91+ async def read_input_registers (self , address , count ):
92+ loop = asyncio .get_running_loop ()
93+ return await loop .run_in_executor (
94+ None ,
95+ self .read_input_registers_sync ,
96+ address ,
97+ count
98+ )
99+
90100 def reset_energy (self ):
91101 with self ._lock :
92102 kwargs = {"slave" : self ._slave }
93103 request = ModbusResetEnergyRequest (** kwargs )
94104 self ._client .execute (request )
95105
96- def info_gather (self ):
106+ # 异步版本info_gather
107+ async def info_gather (self ):
97108 data = {}
98109 try :
99- result = self .read_input_registers (0 , 9 )
110+ result = await self .read_input_registers (0 , 9 )
100111 if result is not None and type (result ) is not ModbusIOException \
101112 and result .registers is not None and len (result .registers ) == 9 :
102113 data [DEVICE_CLASS_VOLTAGE ] = result .registers [0 ] / 10
@@ -106,7 +117,9 @@ def info_gather(self):
106117 data [DEVICE_CLASS_FREQUENCY ] = result .registers [7 ] / 10
107118 data [DEVICE_CLASS_POWER_FACTOR ] = result .registers [8 ] / 100
108119 else :
109- _LOGGER .debug (f "Error in gathering, timed out" )
120+ _LOGGER .debug ("Error in gathering, timed out" )
110121 except Exception as e :
111122 _LOGGER .error (f"Error in gathering, { e } " )
112123 return data
124+
125+ # 如果有别的同步读写函数,也建议用类似方式异步包装
0 commit comments