feat: display MAC address and local IP in AirSend Box device info
This commit is contained in:
+356
-310
@@ -1,310 +1,356 @@
|
||||
"""AirSend device."""
|
||||
import logging
|
||||
import json
|
||||
import hashlib
|
||||
import aiohttp
|
||||
from . import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(DOMAIN)
|
||||
|
||||
RTYPE_LABELS = {
|
||||
0: "AirSend",
|
||||
1: "AirSend Sensor",
|
||||
4096: "AirSend Button",
|
||||
4097: "AirSend Switch",
|
||||
4098: "AirSend Cover",
|
||||
4099: "AirSend Cover (position)",
|
||||
4100: "AirSend Light",
|
||||
}
|
||||
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class TransferResult(Enum):
|
||||
"""Result of an async_transfer call."""
|
||||
SUCCESS = "success" # Command sent and confirmed
|
||||
SENT = "sent" # Command sent, no confirmation (wait=True + 500)
|
||||
SERVER_ERROR = "server_error" # Server responded with error (command may have been sent)
|
||||
NETWORK_ERROR = "network_error" # Addon unreachable (timeout, connection refused)
|
||||
|
||||
|
||||
class Device:
|
||||
"""Representation of a Device."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
options,
|
||||
serviceurl: str,
|
||||
) -> None:
|
||||
"""Initialize a device."""
|
||||
self._name = name
|
||||
self._serviceurl = serviceurl
|
||||
self._uid = None
|
||||
self._rtype = None
|
||||
self._apikey = None
|
||||
self._spurl = None
|
||||
self._wait = False
|
||||
self._channel = {}
|
||||
self._note = None
|
||||
self._bind = None
|
||||
self._refresh = 5 * 60
|
||||
try:
|
||||
self._uid = options["id"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._rtype = options["type"]
|
||||
except KeyError:
|
||||
pass
|
||||
if self._rtype == 0:
|
||||
self._channel = {"id": 1}
|
||||
try:
|
||||
self._apikey = options["apiKey"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._spurl = options["spurl"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._wait = eval(str(options["wait"]))
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._channel = options["channel"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._note = options["note"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._bind = int(options["bind"])
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._refresh = int(options["refresh"])
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._invert = bool(options["invert"])
|
||||
except KeyError:
|
||||
self._invert = False
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unique_channel_name(self) -> str:
|
||||
if self._uid:
|
||||
return str(self._uid)
|
||||
if self._channel:
|
||||
result = str(self._channel['id'])
|
||||
if result:
|
||||
uniquefield = ['source', 'mac', 'seed']
|
||||
for field in uniquefield:
|
||||
if field in self._channel:
|
||||
result += "_"
|
||||
result += str(self._channel[field])
|
||||
return result
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def device_info(self) -> dict:
|
||||
"""Return device info for Home Assistant device registry."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.unique_channel_name)},
|
||||
"name": self._name,
|
||||
"manufacturer": "AirSend",
|
||||
"model": RTYPE_LABELS.get(self._rtype, "AirSend"),
|
||||
}
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
if self._channel:
|
||||
return {"channel": self._channel}
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_async(self) -> bool:
|
||||
"""Return if asynchronous state."""
|
||||
return self._wait == False
|
||||
|
||||
@property
|
||||
def is_airsend(self) -> bool:
|
||||
return self._rtype == 0
|
||||
|
||||
@property
|
||||
def is_sensor(self) -> bool:
|
||||
return self._rtype == 1
|
||||
|
||||
@property
|
||||
def is_button(self) -> bool:
|
||||
return self._rtype == 4096
|
||||
|
||||
@property
|
||||
def is_cover(self) -> bool:
|
||||
return self._rtype in (4098, 4099)
|
||||
|
||||
@property
|
||||
def is_cover_with_position(self) -> bool:
|
||||
return self._rtype == 4099
|
||||
|
||||
@property
|
||||
def is_switch(self) -> bool:
|
||||
return self._rtype == 4097
|
||||
|
||||
@property
|
||||
def is_light(self) -> bool:
|
||||
return self._rtype == 4100
|
||||
|
||||
@property
|
||||
def is_inverted(self) -> bool:
|
||||
"""Return True if open/close logic is inverted."""
|
||||
return self._invert
|
||||
|
||||
@property
|
||||
def refresh_value(self) -> int:
|
||||
"""Return refresh value in seconds."""
|
||||
if isinstance(self._refresh, int) and self._refresh > 0:
|
||||
return self._refresh
|
||||
return 5 * 60
|
||||
|
||||
async def async_bind(self) -> bool:
|
||||
"""Bind a channel to listen (async)."""
|
||||
if not (self._serviceurl and self._spurl and isinstance(self._bind, int) and self._bind > 0):
|
||||
return False
|
||||
payload = json.dumps({
|
||||
"channel": {"id": self._bind},
|
||||
"duration": 0,
|
||||
"callback": "http://127.0.0.1/"
|
||||
})
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self._spurl,
|
||||
"content-type": "application/json",
|
||||
"User-Agent": "hass_airsend",
|
||||
}
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
self._serviceurl + "airsend/bind",
|
||||
headers=headers,
|
||||
data=payload,
|
||||
timeout=aiohttp.ClientTimeout(total=6),
|
||||
) as response:
|
||||
return response.status == 200
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.debug("Bind error '%s': %s", self._name, err)
|
||||
return False
|
||||
|
||||
async def async_transfer(self, note, entity_id=None) -> "TransferResult":
|
||||
"""Send a command (async)."""
|
||||
status_code = 404
|
||||
ret = False
|
||||
wait = 'false, "callback":"http://127.0.0.1/"'
|
||||
if self._wait:
|
||||
wait = 'true'
|
||||
|
||||
if self._serviceurl and self._spurl and entity_id is not None:
|
||||
uid = hashlib.sha256(entity_id.encode('utf-8')).hexdigest()[:12]
|
||||
jnote = json.dumps(note)
|
||||
if (
|
||||
self._note is not None
|
||||
and all(k in self._note for k in ("method", "type", "value"))
|
||||
and all(k in note for k in ("method", "type", "value"))
|
||||
and note["method"] == 1 and note["type"] == 0
|
||||
and note["value"] in ("TOGGLE", 6)
|
||||
):
|
||||
jnote = json.dumps(self._note)
|
||||
|
||||
payload = (
|
||||
'{"wait": ' + wait + ', "channel":'
|
||||
+ json.dumps(self._channel)
|
||||
+ ', "thingnotes":{"uid":"0x' + uid + '", "notes":['
|
||||
+ jnote
|
||||
+ "]}}"
|
||||
)
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self._spurl,
|
||||
"content-type": "application/json",
|
||||
"User-Agent": "hass_airsend",
|
||||
}
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
self._serviceurl + "airsend/transfer",
|
||||
headers=headers,
|
||||
data=payload,
|
||||
timeout=aiohttp.ClientTimeout(total=6),
|
||||
) as response:
|
||||
if self._wait:
|
||||
ret = True
|
||||
status_code = 500
|
||||
try:
|
||||
jdata = await response.json(content_type=None)
|
||||
if jdata.get("type", 0x100) < 0x100:
|
||||
status_code = response.status
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
ret = None
|
||||
status_code = response.status
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.debug("Transfer local error '%s': %s", self._name, err)
|
||||
return TransferResult.NETWORK_ERROR
|
||||
|
||||
# Fallback to cloud API if local failed
|
||||
if status_code != 200 and self._apikey:
|
||||
action = "command"
|
||||
value = 6
|
||||
if all(k in note for k in ("method", "type", "value")):
|
||||
if note["method"] == 1 and note["type"] == 0:
|
||||
value = {
|
||||
"OFF": 0, "ON": 1, "STOP": 3, "DOWN": 4, "UP": 5
|
||||
}.get(note["value"], 6)
|
||||
elif note["method"] == 1 and note["type"] == 9:
|
||||
action = "level"
|
||||
value = int(note["value"])
|
||||
|
||||
cloud_url = (
|
||||
"https://airsend.cloud/device/"
|
||||
+ str(self._uid) + "/" + action + "/" + str(value) + "/"
|
||||
)
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self._apikey,
|
||||
"content-type": "application/json",
|
||||
"User-Agent": "hass_airsend",
|
||||
}
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
cloud_url,
|
||||
headers=headers,
|
||||
timeout=aiohttp.ClientTimeout(total=10),
|
||||
) as response:
|
||||
status_code = response.status
|
||||
ret = True
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.warning("Transfer cloud error '%s': %s — both local and cloud failed", self._name, err)
|
||||
return TransferResult.NETWORK_ERROR
|
||||
|
||||
if status_code == 200:
|
||||
return TransferResult.SUCCESS
|
||||
if status_code == 401:
|
||||
_LOGGER.error("Transfer '%s' : invalid locator (401) — check spurl in airsend.yaml", self.name)
|
||||
return TransferResult.SERVER_ERROR
|
||||
if status_code == 405:
|
||||
_LOGGER.error("Transfer '%s' : invalid input (405) — check channel configuration", self.name)
|
||||
return TransferResult.SERVER_ERROR
|
||||
if status_code == 500:
|
||||
if self._wait:
|
||||
_LOGGER.warning("Transfer '%s' : no RF confirmation (500), command may have been sent", self.name)
|
||||
return TransferResult.SENT
|
||||
_LOGGER.warning("Transfer '%s' : server error (500), command may have been sent", self.name)
|
||||
return TransferResult.SERVER_ERROR
|
||||
_LOGGER.warning("Transfer '%s' : unexpected status %s", self.name, status_code)
|
||||
return TransferResult.SERVER_ERROR
|
||||
"""AirSend device."""
|
||||
import logging
|
||||
import json
|
||||
import hashlib
|
||||
import re
|
||||
import aiohttp
|
||||
from . import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(DOMAIN)
|
||||
|
||||
RTYPE_LABELS = {
|
||||
0: "AirSend Box",
|
||||
1: "AirSend Sensor",
|
||||
4096: "AirSend Button",
|
||||
4097: "AirSend Switch",
|
||||
4098: "AirSend Cover",
|
||||
4099: "AirSend Cover (position)",
|
||||
4100: "AirSend Light",
|
||||
}
|
||||
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class TransferResult(Enum):
|
||||
"""Result of an async_transfer call."""
|
||||
SUCCESS = "success" # Command sent and confirmed
|
||||
SENT = "sent" # Command sent, no confirmation (wait=True + 500)
|
||||
SERVER_ERROR = "server_error" # Server responded with error (command may have been sent)
|
||||
NETWORK_ERROR = "network_error" # Addon unreachable (timeout, connection refused)
|
||||
|
||||
|
||||
class Device:
|
||||
"""Representation of a Device."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
options,
|
||||
serviceurl: str,
|
||||
) -> None:
|
||||
"""Initialize a device."""
|
||||
self._name = name
|
||||
self._serviceurl = serviceurl
|
||||
self._uid = None
|
||||
self._rtype = None
|
||||
self._apikey = None
|
||||
self._spurl = None
|
||||
self._wait = False
|
||||
self._channel = {}
|
||||
self._note = None
|
||||
self._bind = None
|
||||
self._refresh = 5 * 60
|
||||
try:
|
||||
self._uid = options["id"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._rtype = options["type"]
|
||||
except KeyError:
|
||||
pass
|
||||
if self._rtype == 0:
|
||||
self._channel = {"id": 1}
|
||||
try:
|
||||
self._apikey = options["apiKey"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._spurl = options["spurl"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._wait = eval(str(options["wait"]))
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._channel = options["channel"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._note = options["note"]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._bind = int(options["bind"])
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._refresh = int(options["refresh"])
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self._invert = bool(options["invert"])
|
||||
except KeyError:
|
||||
self._invert = False
|
||||
|
||||
# MAC and local IP — only for AirSend Box (type 0)
|
||||
# MAC derived from EUI-64 link-local IPv6 in spurl: sp://TOKEN@[fe80::xxxx]?gw=0&rhost=192.168.x.x
|
||||
self._mac = None
|
||||
self._local_ip = None
|
||||
if self._rtype == 0 and self._spurl:
|
||||
try:
|
||||
ipv6_match = re.search(r'@\[([^\]]+)\]', self._spurl)
|
||||
if ipv6_match:
|
||||
self._mac = self._ipv6_link_local_to_mac(ipv6_match.group(1))
|
||||
rhost_match = re.search(r'rhost=([^&]+)', self._spurl)
|
||||
if rhost_match:
|
||||
self._local_ip = rhost_match.group(1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _ipv6_link_local_to_mac(ipv6_str: str) -> str:
|
||||
"""Convert a fe80:: EUI-64 link-local IPv6 address to a MAC address."""
|
||||
ipv6_str = ipv6_str.lower().strip()
|
||||
if '::' in ipv6_str:
|
||||
left, right = ipv6_str.split('::')
|
||||
left_groups = left.split(':') if left else []
|
||||
right_groups = right.split(':') if right else []
|
||||
missing = 8 - len(left_groups) - len(right_groups)
|
||||
groups = left_groups + ['0'] * missing + right_groups
|
||||
else:
|
||||
groups = ipv6_str.split(':')
|
||||
groups = [g.zfill(4) for g in groups]
|
||||
eui64 = ''.join(groups[4:])
|
||||
b = [int(eui64[i:i+2], 16) for i in range(0, 16, 2)]
|
||||
mac_bytes = [b[0] ^ 0x02, b[1], b[2], b[5], b[6], b[7]]
|
||||
return ':'.join(f'{x:02X}' for x in mac_bytes)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unique_channel_name(self) -> str:
|
||||
if self._uid:
|
||||
return str(self._uid)
|
||||
if self._channel:
|
||||
result = str(self._channel['id'])
|
||||
if result:
|
||||
uniquefield = ['source', 'mac', 'seed']
|
||||
for field in uniquefield:
|
||||
if field in self._channel:
|
||||
result += "_"
|
||||
result += str(self._channel[field])
|
||||
return result
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def device_info(self) -> dict:
|
||||
"""Return device info for Home Assistant device registry."""
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
connections = set()
|
||||
if self._mac:
|
||||
connections.add((dr.CONNECTION_NETWORK_MAC, self._mac))
|
||||
|
||||
info = {
|
||||
"identifiers": {(DOMAIN, self.unique_channel_name)},
|
||||
"name": self._name,
|
||||
"manufacturer": "AirSend",
|
||||
"model": RTYPE_LABELS.get(self._rtype, "AirSend"),
|
||||
}
|
||||
if connections:
|
||||
info["connections"] = connections
|
||||
info["configuration_url"] = "https://app.airsend.cloud/"
|
||||
if self._local_ip:
|
||||
info["hw_version"] = f"IP: {self._local_ip}"
|
||||
return info
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
if self._channel:
|
||||
return {"channel": self._channel}
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_async(self) -> bool:
|
||||
"""Return if asynchronous state."""
|
||||
return self._wait == False
|
||||
|
||||
@property
|
||||
def is_airsend(self) -> bool:
|
||||
return self._rtype == 0
|
||||
|
||||
@property
|
||||
def is_sensor(self) -> bool:
|
||||
return self._rtype == 1
|
||||
|
||||
@property
|
||||
def is_button(self) -> bool:
|
||||
return self._rtype == 4096
|
||||
|
||||
@property
|
||||
def is_cover(self) -> bool:
|
||||
return self._rtype in (4098, 4099)
|
||||
|
||||
@property
|
||||
def is_cover_with_position(self) -> bool:
|
||||
return self._rtype == 4099
|
||||
|
||||
@property
|
||||
def is_switch(self) -> bool:
|
||||
return self._rtype == 4097
|
||||
|
||||
@property
|
||||
def is_light(self) -> bool:
|
||||
return self._rtype == 4100
|
||||
|
||||
@property
|
||||
def is_inverted(self) -> bool:
|
||||
"""Return True if open/close logic is inverted."""
|
||||
return self._invert
|
||||
|
||||
@property
|
||||
def refresh_value(self) -> int:
|
||||
"""Return refresh value in seconds."""
|
||||
if isinstance(self._refresh, int) and self._refresh > 0:
|
||||
return self._refresh
|
||||
return 5 * 60
|
||||
|
||||
async def async_bind(self) -> bool:
|
||||
"""Bind a channel to listen (async)."""
|
||||
if not (self._serviceurl and self._spurl and isinstance(self._bind, int) and self._bind > 0):
|
||||
return False
|
||||
payload = json.dumps({
|
||||
"channel": {"id": self._bind},
|
||||
"duration": 0,
|
||||
"callback": "http://127.0.0.1/"
|
||||
})
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self._spurl,
|
||||
"content-type": "application/json",
|
||||
"User-Agent": "hass_airsend",
|
||||
}
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
self._serviceurl + "airsend/bind",
|
||||
headers=headers,
|
||||
data=payload,
|
||||
timeout=aiohttp.ClientTimeout(total=6),
|
||||
) as response:
|
||||
return response.status == 200
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.debug("Bind error '%s': %s", self._name, err)
|
||||
return False
|
||||
|
||||
async def async_transfer(self, note, entity_id=None) -> "TransferResult":
|
||||
"""Send a command (async)."""
|
||||
status_code = 404
|
||||
ret = False
|
||||
wait = 'false, "callback":"http://127.0.0.1/"'
|
||||
if self._wait:
|
||||
wait = 'true'
|
||||
|
||||
if self._serviceurl and self._spurl and entity_id is not None:
|
||||
uid = hashlib.sha256(entity_id.encode('utf-8')).hexdigest()[:12]
|
||||
jnote = json.dumps(note)
|
||||
if (
|
||||
self._note is not None
|
||||
and all(k in self._note for k in ("method", "type", "value"))
|
||||
and all(k in note for k in ("method", "type", "value"))
|
||||
and note["method"] == 1 and note["type"] == 0
|
||||
and note["value"] in ("TOGGLE", 6)
|
||||
):
|
||||
jnote = json.dumps(self._note)
|
||||
|
||||
payload = (
|
||||
'{"wait": ' + wait + ', "channel":'
|
||||
+ json.dumps(self._channel)
|
||||
+ ', "thingnotes":{"uid":"0x' + uid + '", "notes":['
|
||||
+ jnote
|
||||
+ "]}}"
|
||||
)
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self._spurl,
|
||||
"content-type": "application/json",
|
||||
"User-Agent": "hass_airsend",
|
||||
}
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
self._serviceurl + "airsend/transfer",
|
||||
headers=headers,
|
||||
data=payload,
|
||||
timeout=aiohttp.ClientTimeout(total=6),
|
||||
) as response:
|
||||
if self._wait:
|
||||
ret = True
|
||||
status_code = 500
|
||||
try:
|
||||
jdata = await response.json(content_type=None)
|
||||
if jdata.get("type", 0x100) < 0x100:
|
||||
status_code = response.status
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
ret = None
|
||||
status_code = response.status
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.debug("Transfer local error '%s': %s", self._name, err)
|
||||
return TransferResult.NETWORK_ERROR
|
||||
|
||||
# Fallback to cloud API if local failed
|
||||
if status_code != 200 and self._apikey:
|
||||
action = "command"
|
||||
value = 6
|
||||
if all(k in note for k in ("method", "type", "value")):
|
||||
if note["method"] == 1 and note["type"] == 0:
|
||||
value = {
|
||||
"OFF": 0, "ON": 1, "STOP": 3, "DOWN": 4, "UP": 5
|
||||
}.get(note["value"], 6)
|
||||
elif note["method"] == 1 and note["type"] == 9:
|
||||
action = "level"
|
||||
value = int(note["value"])
|
||||
|
||||
cloud_url = (
|
||||
"https://airsend.cloud/device/"
|
||||
+ str(self._uid) + "/" + action + "/" + str(value) + "/"
|
||||
)
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self._apikey,
|
||||
"content-type": "application/json",
|
||||
"User-Agent": "hass_airsend",
|
||||
}
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
cloud_url,
|
||||
headers=headers,
|
||||
timeout=aiohttp.ClientTimeout(total=10),
|
||||
) as response:
|
||||
status_code = response.status
|
||||
ret = True
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.warning("Transfer cloud error '%s': %s — both local and cloud failed", self._name, err)
|
||||
return TransferResult.NETWORK_ERROR
|
||||
|
||||
if status_code == 200:
|
||||
return TransferResult.SUCCESS
|
||||
if status_code == 401:
|
||||
_LOGGER.error("Transfer '%s' : invalid locator (401) — check spurl in airsend.yaml", self.name)
|
||||
return TransferResult.SERVER_ERROR
|
||||
if status_code == 405:
|
||||
_LOGGER.error("Transfer '%s' : invalid input (405) — check channel configuration", self.name)
|
||||
return TransferResult.SERVER_ERROR
|
||||
if status_code == 500:
|
||||
if self._wait:
|
||||
_LOGGER.warning("Transfer '%s' : no RF confirmation (500), command may have been sent", self.name)
|
||||
return TransferResult.SENT
|
||||
_LOGGER.warning("Transfer '%s' : server error (500), command may have been sent", self.name)
|
||||
return TransferResult.SERVER_ERROR
|
||||
_LOGGER.warning("Transfer '%s' : unexpected status %s", self.name, status_code)
|
||||
return TransferResult.SERVER_ERROR
|
||||
Reference in New Issue
Block a user