Skip to content

Commit

Permalink
Merge pull request #132 from puddly/rc
Browse files Browse the repository at this point in the history
0.10.0 Release
  • Loading branch information
puddly authored Oct 3, 2022
2 parents 3f7898e + a6c2952 commit a78951d
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 104 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Set up Python 3.7
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
version: 3.7
version: 3.8
- name: Install wheel
run: >-
pip install wheel
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.8", "3.9", "3.10"]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
'pyserial-asyncio>=0.5; platform_system!="Windows"',
'pyserial-asyncio!=0.5; platform_system=="Windows"', # 0.5 broke writes
'pyusb>=1.1.0',
'zigpy>=0.47.0',
'zigpy>=0.51.0',
'gpiozero',
],
tests_require=[
Expand Down
4 changes: 2 additions & 2 deletions zigpy_zigate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
MAJOR_VERSION = 0
MINOR_VERSION = 9
PATCH_VERSION = '2'
MINOR_VERSION = 10
PATCH_VERSION = '0'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
7 changes: 5 additions & 2 deletions zigpy_zigate/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class ResponseId(enum.IntEnum):
EXTENDED_ERROR = 0x9999


class SendSecurity(t.uint8_t, enum.Enum):
NETWORK = 0x00
APPLINK = 0x01
TEMP_APPLINK = 0x02


class NonFactoryNewRestartStatus(t.uint8_t, enum.Enum):
Expand Down Expand Up @@ -482,11 +486,10 @@ async def remove_device(self, zigate_ieee, ieee):
return await self.command(CommandId.NETWORK_REMOVE_DEVICE, data)

async def raw_aps_data_request(self, addr, src_ep, dst_ep, profile,
cluster, payload, addr_mode=2, security=0):
cluster, payload, addr_mode=t.AddressMode.NWK, security=SendSecurity.NETWORK, radius=0):
'''
Send raw APS Data request
'''
radius = 0
data = t.serialize([addr_mode, addr,
src_ep, dst_ep, cluster, profile,
security, radius, payload], COMMANDS[CommandId.SEND_RAW_APS_DATA_PACKET])
Expand Down
55 changes: 46 additions & 9 deletions zigpy_zigate/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,20 @@ def __str__(self):
class AddressMode(uint8_t, enum.Enum):
# Address modes used in zigate protocol

BOUND = 0x00
GROUP = 0x01
NWK = 0x02
IEEE = 0x03
BROADCAST = 0x04

NO_TRANSMIT = 0x05

BOUND_NO_ACK = 0x06
NWK_NO_ACK = 0x07
IEEE_NO_ACK = 0x08

BOUND_NON_BLOCKING = 0x09
BOUND_NON_BLOCKING_NO_ACK = 0x0A


class Status(uint8_t, enum.Enum):
Expand Down Expand Up @@ -259,6 +270,26 @@ def __repr__(self):
return r


ZIGPY_TO_ZIGATE_ADDR_MODE = {
# With ACKs
(zigpy.types.AddrMode.NWK, True): AddressMode.NWK,
(zigpy.types.AddrMode.IEEE, True): AddressMode.IEEE,
(zigpy.types.AddrMode.Broadcast, True): AddressMode.BROADCAST,
(zigpy.types.AddrMode.Group, True): AddressMode.GROUP,

# Without ACKs
(zigpy.types.AddrMode.NWK, False): AddressMode.NWK_NO_ACK,
(zigpy.types.AddrMode.IEEE, False): AddressMode.IEEE_NO_ACK,
(zigpy.types.AddrMode.Broadcast, False): AddressMode.BROADCAST,
(zigpy.types.AddrMode.Group, False): AddressMode.GROUP,
}

ZIGATE_TO_ZIGPY_ADDR_MODE = {
zigate_addr: (zigpy_addr, ack)
for (zigpy_addr, ack), zigate_addr in ZIGPY_TO_ZIGATE_ADDR_MODE.items()
}


class Address(Struct):
_fields = [
('address_mode', AddressMode),
Expand All @@ -271,17 +302,23 @@ def __eq__(self, other):
@classmethod
def deserialize(cls, data):
r = cls()
field_name, field_type = cls._fields[0]
mode, data = field_type.deserialize(data)
setattr(r, field_name, mode)
v = None
if mode in [AddressMode.GROUP, AddressMode.NWK]:
v, data = NWK.deserialize(data)
elif mode == AddressMode.IEEE:
v, data = EUI64.deserialize(data)
setattr(r, cls._fields[1][0], v)
r.address_mode, data = AddressMode.deserialize(data)

if r.address_mode in (AddressMode.IEEE, AddressMode.IEEE_NO_ACK):
r.address, data = EUI64.deserialize(data)
else:
r.address, data = NWK.deserialize(data)

return r, data

def to_zigpy_type(self):
zigpy_addr_mode, ack = ZIGATE_TO_ZIGPY_ADDR_MODE[self.address_mode]

return (
zigpy.types.AddrModeAddress(addr_mode=zigpy_addr_mode, address=self.address),
ack,
)


class DeviceEntry(Struct):
_fields = [
Expand Down
53 changes: 19 additions & 34 deletions zigpy_zigate/uart.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import struct
from typing import Any, Dict

import serial # noqa
import serial.tools.list_ports
import serial_asyncio
import zigpy.serial

from .config import CONF_DEVICE_PATH
from . import common as c
Expand Down Expand Up @@ -141,38 +139,25 @@ async def connect(device_config: Dict[str, Any], api, loop=None):
if port == 'auto':
port = c.discover_port()

if c.is_zigate_wifi(port):
if c.is_pizigate(port):
LOGGER.debug('PiZiGate detected')
await c.async_set_pizigate_running_mode()
# in case of pizigate:/dev/ttyAMA0 syntax
if port.startswith('pizigate:'):
port = port.replace('pizigate:', '', 1)
elif c.is_zigate_din(port):
LOGGER.debug('ZiGate USB DIN detected')
await c.async_set_zigatedin_running_mode()
elif c.is_zigate_wifi(port):
LOGGER.debug('ZiGate WiFi detected')
port = port.split('socket://', 1)[1]
if ':' in port:
host, port = port.split(':', 1) # 192.168.x.y:9999
port = int(port)
else:
host = port
port = 9999
_, protocol = await loop.create_connection(
lambda: protocol,
host, port)
else:
if c.is_pizigate(port):
LOGGER.debug('PiZiGate detected')
await c.async_set_pizigate_running_mode()
# in case of pizigate:/dev/ttyAMA0 syntax
if port.startswith('pizigate:'):
port = port[9:]
elif c.is_zigate_din(port):
LOGGER.debug('ZiGate USB DIN detected')
await c.async_set_zigatedin_running_mode()

_, protocol = await serial_asyncio.create_serial_connection(
loop,
lambda: protocol,
url=port,
baudrate=ZIGATE_BAUDRATE,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
xonxoff=False,
)

_, protocol = await zigpy.serial.create_serial_connection(
loop,
lambda: protocol,
url=port,
baudrate=ZIGATE_BAUDRATE,
xonxoff=False,
)

await connected_future

Expand Down
117 changes: 64 additions & 53 deletions zigpy_zigate/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,13 @@ async def load_network_info(self, *, load_devices: bool = False):
self.state.network_info.children.append(ieee)
self.state.network_info.nwk_addresses[ieee] = zigpy.types.NWK(device.short_addr)

async def reset_network_info(self):
await self._api.erase_persistent_data()

async def write_network_info(self, *, network_info, node_info):
LOGGER.warning('Setting the pan_id is not supported by ZiGate')

await self._api.erase_persistent_data()

await self.reset_network_info()
await self._api.set_channel(network_info.channel)

epid, _ = zigpy.types.uint64_t.deserialize(network_info.extended_pan_id.serialize())
Expand Down Expand Up @@ -179,28 +181,30 @@ def zigate_callback_handler(self, msg, response, lqi):
# LOGGER.debug('Start pairing {} (1st device announce)'.format(nwk))
# self._pending_join.append(nwk)
elif msg == ResponseId.DATA_INDICATION:
if response[1] == 0x0 and response[2] == 0x13:
nwk = zigpy.types.NWK(response[5].address)
ieee = zigpy.types.EUI64(response[7][3:11])
parent_nwk = 0
self.handle_join(nwk, ieee, parent_nwk)
return
try:
if response[5].address_mode == t.AddressMode.NWK:
device = self.get_device(nwk = zigpy.types.NWK(response[5].address))
elif response[5].address_mode == t.AddressMode.IEEE:
device = self.get_device(ieee=zigpy.types.EUI64(response[5].address))
else:
LOGGER.error("No such device %s", response[5].address)
return
except KeyError:
LOGGER.debug("No such device %s", response[5].address)
return
rssi = 0
device.radio_details(lqi, rssi)
self.handle_message(device, response[1],
response[2],
response[3], response[4], response[-1])
(
status,
profile_id,
cluster_id,
src_ep,
dst_ep,
src,
dst,
payload,
) = response

packet = zigpy.types.ZigbeePacket(
src=src.to_zigpy_type()[0],
src_ep=src_ep,
dst=dst.to_zigpy_type()[0],
dst_ep=dst_ep,
profile_id=profile_id,
cluster_id=cluster_id,
data=zigpy.types.SerializableBytes(payload),
lqi=lqi,
rssi=None,
)

self.packet_received(packet)
elif msg == ResponseId.ACK_DATA:
LOGGER.debug('ACK Data received %s %s', response[4], response[0])
# disabled because of https://github.com/fairecasoimeme/ZiGate/issues/324
Expand Down Expand Up @@ -228,53 +232,60 @@ def _handle_frame_failure(self, message_tag, status):
except asyncio.futures.InvalidStateError as exc:
LOGGER.debug("Invalid state on future - probably duplicate response: %s", exc)

@zigpy.util.retryable_request
async def request(self, device, profile, cluster, src_ep, dst_ep, sequence, data,
expect_reply=True, use_ieee=False):
return await self._request(device.nwk, profile, cluster, src_ep, dst_ep, sequence, data,
expect_reply, use_ieee)

async def mrequest(self, group_id, profile, cluster, src_ep, sequence, data, *, hops=0, non_member_radius=3):
src_ep = 1
return await self._request(group_id, profile, cluster, src_ep, src_ep, sequence, data, addr_mode=1)

async def _request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, data,
expect_reply=True, use_ieee=False, addr_mode=2):
src_ep = 1 if dst_ep else 0 # ZiGate only support endpoint 1
LOGGER.debug('request %s',
(nwk, profile, cluster, src_ep, dst_ep, sequence, data, expect_reply, use_ieee))
async def send_packet(self, packet):
LOGGER.debug("Sending packet %r", packet)

if packet.dst.addr_mode == zigpy.types.AddrMode.IEEE:
LOGGER.warning("IEEE addressing is not supported, falling back to NWK")

try:
device = self.get_device_with_address(packet.dst)
except (KeyError, ValueError):
raise ValueError(f"Cannot find device with IEEE {packet.dst.address}")

packet = packet.replace(
dst=zigpy.types.AddrModeAddress(
addr_mode=zigpy.types.AddrMode.NWK, address=device.nwk
)
)

ack = (zigpy.types.TransmitOptions.ACK in packet.tx_options)

try:
v, lqi = await self._api.raw_aps_data_request(nwk, src_ep, dst_ep, profile, cluster, data, addr_mode)
(status, tsn, packet_type, _), _ = await self._api.raw_aps_data_request(
addr=packet.dst.address,
src_ep=(1 if packet.dst_ep > 0 else 0), # ZiGate only support endpoint 1
dst_ep=packet.dst_ep,
profile=packet.profile_id,
cluster=packet.cluster_id,
payload=packet.data.serialize(),
addr_mode=t.ZIGPY_TO_ZIGATE_ADDR_MODE[packet.dst.addr_mode, ack],
radius=packet.radius,
)
except NoResponseError:
return 1, "ZiGate doesn't answer to command"
req_id = v[1]
send_fut = asyncio.Future()
self._pending[req_id] = send_fut
raise zigpy.exceptions.DeliveryError("ZiGate did not respond to command")

if v[0] != t.Status.Success:
self._pending.pop(req_id)
return v[0], "Message send failure {}".format(v[0])
if status != t.Status.Success:
self._pending.pop(tsn)
raise zigpy.exceptions.DeliveryError(f"Failed to deliver packet: {status}", status=status)

self._pending[tsn] = asyncio.get_running_loop().create_future()

# disabled because of https://github.com/fairecasoimeme/ZiGate/issues/324
# try:
# v = await asyncio.wait_for(send_fut, 120)
# except asyncio.TimeoutError:
# return 1, "timeout waiting for message %s send ACK" % (sequence, )
# finally:
# self._pending.pop(req_id)
# self._pending.pop(tsn)
# return v, "Message sent"
return 0, "Message sent"

async def permit_ncp(self, time_s=60):
assert 0 <= time_s <= 254
status, lqi = await self._api.permit_join(time_s)
if status[0] != t.Status.Success:
await self._api.reset()

async def broadcast(self, profile, cluster, src_ep, dst_ep, grpid, radius,
sequence, data, broadcast_address):
LOGGER.debug("Broadcast not implemented.")


class ZiGateDevice(zigpy.device.Device):
def __init__(self, application, ieee, nwk):
Expand Down

0 comments on commit a78951d

Please sign in to comment.