88from pymodbus .constants import DeviceInformation # type: ignore
99from pymodbus .pdu import ExceptionResponse # type: ignore
1010from pymodbus .client .mixin import ModbusClientMixin # type: ignore
11+ from pymodbus .exceptions import ModbusIOException # type: ignore
1112
1213GATEWAY_SLAVE_ID = 255
1314SYNTHESIS_TABLE_SLAVE_ID_START = 247
@@ -249,26 +250,26 @@ async def firmware_version(self) -> str | None:
249250
250251 # Status
251252
252- async def status (self ) -> LinkStatus :
253+ async def status (self ) -> LinkStatus | None :
253254 """PowerTag Link gateway status and diagnostic register"""
254255 assert self .type_of_gateway in [
255256 TypeOfGateway .POWERTAG_LINK ,
256257 TypeOfGateway .SMARTLINK ,
257258 ]
258259 bitmap = self .__read_int_16 (0x0070 , GATEWAY_SLAVE_ID )
259260 try :
260- return LinkStatus (bitmap )
261+ return LinkStatus (bitmap ) if bitmap is not None else None
261262 except Exception as e :
262263 _LOGGER .error (
263264 f"Could not map status, defaulting to GENERAL_FAILURE ({ str (e )} "
264265 )
265266 return LinkStatus .GENERAL_FAILURE
266267
267- async def health (self ) -> PanelHealth :
268+ async def health (self ) -> PanelHealth | None :
268269 """PowerTag Link gateway status and diagnostic register"""
269270 assert self .type_of_gateway == TypeOfGateway .PANEL_SERVER
270271 code = await self .__read_int_16 (0x009E , GATEWAY_SLAVE_ID )
271- return PanelHealth (code )
272+ return PanelHealth (code ) if code is not None else None
272273
273274 # Date and Time
274275
@@ -332,9 +333,10 @@ async def tag_power_factor_total(self, tag_index: int) -> float | None:
332333
333334 async def tag_power_factor_sign_convention (
334335 self , tag_index : int
335- ) -> PowerFactorSignConvention :
336+ ) -> PowerFactorSignConvention | None :
336337 """Power factor sign convention"""
337- return PowerFactorSignConvention (await self .__read_int_16 (0xC0D , tag_index ))
338+ power_factor_sign = await self .__read_int_16 (0xC0D , tag_index )
339+ return PowerFactorSignConvention (power_factor_sign ) if power_factor_sign is not None else None
338340
339341 # Frequency Metering Data
340342
@@ -553,13 +555,18 @@ async def tag_power_active_demand_total_maximum_timestamp(
553555 async def tag_is_alarm_valid (self , tag_index : int ) -> AlarmDetails | bool | None :
554556 """Validity of the alarm bitmap"""
555557 if self .type_of_gateway is TypeOfGateway .PANEL_SERVER :
556- return AlarmDetails (await self .__read_int_32 (0xCE1 , tag_index ) or 0 )
558+ alarm_valid = await self .__read_int_32 (0xCE1 , tag_index )
559+ return AlarmDetails (alarm_valid ) if alarm_valid is not None else None
557560 else :
558- return (await self .__read_int_32 (0xCE1 , tag_index ) & 0b1 ) != 0
561+ alarm_valid = await self .__read_int_32 (0xCE1 , tag_index )
562+ if alarm_valid is None :
563+ return None
564+ return (alarm_valid & 0b1 ) != 0
559565
560- async def tag_get_alarm (self , tag_index : int ) -> AlarmDetails :
566+ async def tag_get_alarm (self , tag_index : int ) -> AlarmDetails | None :
561567 """Alarms"""
562- return AlarmDetails (await self .__read_int_32 (0xCE3 , tag_index ) or 0 )
568+ alarm = await self .__read_int_32 (0xCE3 , tag_index )
569+ return AlarmDetails (alarm ) if alarm is not None else None
563570
564571 async def tag_current_at_voltage_loss (
565572 self , tag_index : int , phase : Phase
@@ -593,21 +600,25 @@ async def tag_circuit(self, tag_index: int) -> str | None:
593600 """Circuit identifier of the wireless device. The user can enter maximum five characters."""
594601 return await self .__read_string (0x7922 , 3 , tag_index )
595602
596- async def tag_usage (self , tag_index : int ) -> DeviceUsage :
603+ async def tag_usage (self , tag_index : int ) -> DeviceUsage | None :
597604 """Indicates the usage of the wireless device."""
598- return DeviceUsage (await self .__read_int_16 (0x7925 , tag_index ))
605+ usage = await self .__read_int_16 (0x7925 , tag_index )
606+ return DeviceUsage (usage ) if usage is not None else None
599607
600- async def tag_phase_sequence (self , tag_index : int ) -> PhaseSequence :
608+ async def tag_phase_sequence (self , tag_index : int ) -> PhaseSequence | None :
601609 """Phase sequence."""
602- return PhaseSequence (await self .__read_int_16 (0x7926 , tag_index ))
610+ sequence = await self .__read_int_16 (0x7926 , tag_index )
611+ return PhaseSequence (sequence ) if sequence is not None else None
603612
604- async def tag_position (self , tag_index : int ) -> Position :
613+ async def tag_position (self , tag_index : int ) -> Position | None :
605614 """Mounting position"""
606- return Position (await self .__read_int_16 (0x7927 , tag_index ))
615+ position = await self .__read_int_16 (0x7927 , tag_index )
616+ return Position (position ) if position is not None else None
607617
608- async def tag_circuit_diagnostic (self , tag_index : int ) -> Position :
618+ async def tag_circuit_diagnostic (self , tag_index : int ) -> Position | None :
609619 """Circuit diagnostics"""
610- return Position (await self .__read_int_16 (0x7928 , tag_index ))
620+ position = await self .__read_int_16 (0x7928 , tag_index )
621+ return Position (position ) if position is not None else None
611622
612623 async def tag_rated_current (self , tag_index : int ) -> int | None :
613624 """Rated current of the protective device to the wireless device"""
@@ -617,6 +628,8 @@ async def tag_electrical_network_system_type(
617628 self , tag_index : int
618629 ) -> ElectricalNetworkSystemType | None :
619630 code = await self .__read_int_16 (0x792A , tag_index )
631+ if code is None :
632+ return None
620633 system_type = [e for e in ElectricalNetworkSystemType if e .value [0 ] == code ]
621634 return system_type [0 ] if system_type else None
622635
@@ -630,11 +643,12 @@ async def tag_reset_peak_demands(self, tag_index: int):
630643 """Reset All Peak Demands"""
631644 await self .__write_int_16 (0x792E , tag_index , 1 )
632645
633- async def tag_power_supply_type (self , tag_index : int ) -> Position :
646+ async def tag_power_supply_type (self , tag_index : int ) -> Position | None :
634647 """Power supply type"""
635648 if self .type_of_gateway == TypeOfGateway .SMARTLINK :
636649 return Position .INVALID
637- return Position (await self .__read_int_16 (0x792F , tag_index ))
650+ position = await self .__read_int_16 (0x792F , tag_index )
651+ return Position (position ) if position is not None else None
638652
639653 # Device identification
640654
@@ -928,16 +942,15 @@ async def __async_read(
928942 timeout = 5.0 ,
929943 )
930944 if result .isError ():
931- _LOGGER .debug (
932- f"Modbus error reading { address } from slave ID { slave_id } "
933- )
945+ _LOGGER .debug (f"Modbus error reading { address } from slave ID { slave_id } " )
934946 return None
935947 return result .registers
936948
937949 except asyncio .TimeoutError :
938- _LOGGER .debug (
939- f"Timeout when fetching address { address } from slave ID { slave_id } "
940- )
950+ _LOGGER .debug (f"Timeout when fetching address { address } from slave ID { slave_id } " )
951+ return None
952+ except ModbusIOException as e :
953+ _LOGGER .error (f"Error when fetching { address } from slave ID { slave_id } : { e } " )
941954 return None
942955
943956 async def __async_write (
@@ -958,6 +971,7 @@ async def __async_write(
958971 _LOGGER .debug (
959972 f"Timeout when writing to address { address } to slave ID { slave_id } "
960973 )
974+ return None
961975
962976 async def __identify (self , _ : int ):
963977 # data = self.client.read_device_information(read_code=DeviceInformation.REGULAR, device_id=0xFF)
@@ -983,6 +997,8 @@ async def __write_string(self, address: int, slave_id: int, string: str):
983997
984998 async def __read_float_32 (self , address : int , slave_id : int ) -> float | None :
985999 registers = await self .__async_read (address , 2 , slave_id )
1000+ if registers is None :
1001+ return None
9861002 result = self .client .convert_from_registers (
9871003 registers , ModbusClientMixin .DATATYPE .FLOAT32
9881004 )
@@ -994,6 +1010,8 @@ async def __read_float_32(self, address: int, slave_id: int) -> float | None:
9941010
9951011 async def __read_int_16 (self , address : int , slave_id : int ) -> int | None :
9961012 registers = await self .__async_read (address , 1 , slave_id )
1013+ if registers is None :
1014+ return None
9971015 result = self .client .convert_from_registers (
9981016 registers , ModbusClientMixin .DATATYPE .UINT16
9991017 )
@@ -1007,13 +1025,17 @@ async def __write_int_16(self, address: int, slave_id: int, value: int):
10071025
10081026 async def __read_int_32 (self , address : int , slave_id : int ) -> int | None :
10091027 registers = await self .__async_read (address , 2 , slave_id )
1028+ if registers is None :
1029+ return None
10101030 result = self .client .convert_from_registers (
10111031 registers , ModbusClientMixin .DATATYPE .UINT32
10121032 )
10131033 return result if result != 0x8000_0000 else None
10141034
10151035 async def __read_int_64 (self , address : int , slave_id : int ) -> int | None :
10161036 registers = await self .__async_read (address , 4 , slave_id )
1037+ if registers is None :
1038+ return None
10171039 result = self .client .convert_from_registers (
10181040 registers , ModbusClientMixin .DATATYPE .UINT64
10191041 )
@@ -1027,6 +1049,8 @@ async def __write_int_64(self, address: int, slave_id: int, value: int):
10271049
10281050 async def __read_date_time (self , address : int , slave_id ) -> datetime | None :
10291051 registers = await self .__async_read (address , 4 , slave_id )
1052+ if registers is None :
1053+ return None
10301054
10311055 year_raw = self .client .convert_from_registers (
10321056 registers [0 :1 ], ModbusClientMixin .DATATYPE .UINT16
0 commit comments