Skip to content

Commit

Permalink
Always send version(4) when resetting (#589)
Browse files Browse the repository at this point in the history
* Do not grow packet buffer if not joined to any network

* Split `write_config` into a parsing method and a writing method

* Grow packet buffers only after the stack is running

* Register endpoints immediately after connecting and resetting

* Always switch back to EZSPv4 after resetting

* v8 no longer crashes now that `version(4)` is sent

* Revert "Split `write_config` into a parsing method and a writing method"

This reverts commit 2125c0f.

* Reduce diff size

* Remove unnecessary unit tests for new behavior
  • Loading branch information
puddly authored Oct 24, 2023
1 parent 9cc1cde commit 29dec53
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 61 deletions.
42 changes: 20 additions & 22 deletions bellows/ezsp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,25 +183,33 @@ async def reset(self):
LOGGER.debug("Resetting EZSP")
self.stop_ezsp()
await self._gw.reset()

# Always switch back to protocol v4 after a reset
self._switch_protocol_version(v4.EZSPv4.VERSION)
self.start_ezsp()

def _switch_protocol_version(self, version: int) -> None:
self._ezsp_version = version
LOGGER.debug("Switching to EZSP protocol version %d", self.ezsp_version)

try:
protcol_cls = self._BY_VERSION[version]
except KeyError:
LOGGER.warning(
"Protocol version %s is not supported, using version %s instead",
version,
EZSP_LATEST,
)
protcol_cls = self._BY_VERSION[EZSP_LATEST]

self._protocol = protcol_cls(self.handle_callback, self._gw)

async def version(self):
ver, stack_type, stack_version = await self._command(
"version", self.ezsp_version
)
if ver != self.ezsp_version:
self._ezsp_version = ver
LOGGER.debug("Switching to EZSP protocol version %d", self.ezsp_version)
try:
protcol_cls = self._BY_VERSION[ver]
except KeyError:
LOGGER.warning(
"Protocol version %s is not supported, using version %s instead",
ver,
EZSP_LATEST,
)
protcol_cls = self._BY_VERSION[EZSP_LATEST]
self._protocol = protcol_cls(self.handle_callback, self._gw)
self._switch_protocol_version(ver)
await self._command("version", ver)
LOGGER.debug(
"EZSP Stack Type: %s, Stack Version: %04x, Protocol version: %s",
Expand Down Expand Up @@ -569,16 +577,6 @@ async def write_config(self, config: dict) -> None:
],
}

# If we are not joined to a network, old FWs crash if we grow the buffer
if self._ezsp_version < 7:
(state,) = await self.networkState()

if state != self.types.EmberNetworkStatus.JOINED_NETWORK:
LOGGER.debug("Skipping growing packet buffer, not on a network")
del ezsp_config[
self.types.EzspConfigId.CONFIG_PACKET_BUFFER_COUNT.name
]

# First, set the values
for cfg in ezsp_values.values():
# XXX: A read failure does not mean the value is not writeable!
Expand Down
9 changes: 1 addition & 8 deletions bellows/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ async def connect(self) -> None:
raise

self._ezsp = ezsp
await self.register_endpoints()

async def _ensure_network_running(self) -> bool:
"""Ensures the network is currently running and returns whether or not the network
Expand Down Expand Up @@ -177,18 +178,10 @@ async def _ensure_network_running(self) -> bool:
async def start_network(self):
ezsp = self._ezsp

try:
await self.register_endpoints()
except StackAlreadyRunning:
# Endpoints can only be registered before the network is up
await self._reset()
await self.register_endpoints()

await self._ensure_network_running()

if await repairs.fix_invalid_tclk_partner_ieee(ezsp):
await self._reset()
await self.register_endpoints()
await self._ensure_network_running()

if self.config[zigpy.config.CONF_SOURCE_ROUTING]:
Expand Down
25 changes: 0 additions & 25 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -1854,28 +1854,3 @@ async def test_repair_tclk_partner_ieee(app: ControllerApplication) -> None:
await app.start_network()

assert len(app._reset.mock_calls) == 1


async def test_startup_endpoint_register_already_running(
app: ControllerApplication, ieee: t.EmberEUI64
) -> None:
"""Test that the host is reset before endpoint registration if it is running."""

app._ezsp = _create_app_for_startup(app, t.EmberNodeType.COORDINATOR, ieee)
app._ezsp.addEndpoint = AsyncMock(
side_effect=[
[t.EmberStatus.INVALID_CALL], # Fail the first time
[t.EmberStatus.SUCCESS],
[t.EmberStatus.SUCCESS],
[t.EmberStatus.SUCCESS],
]
)

app._reset = AsyncMock()

with patch.object(bellows.multicast.Multicast, "startup"):
await app.start_network()

assert len(app._reset.mock_calls) == 1

assert app._ezsp.addEndpoint.call_count >= 3
6 changes: 0 additions & 6 deletions tests/test_ezsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,12 +790,6 @@ async def test_config_initialize_husbzb1(ezsp_f):
await ezsp_f.write_config({})
assert ezsp_f.setConfigurationValue.mock_calls == expected_calls

# If there is no network, `CONFIG_PACKET_BUFFER_COUNT` won't be set
ezsp_f.setConfigurationValue.reset_mock()
ezsp_f.networkState = AsyncMock(return_value=(t.EmberNetworkStatus.NO_NETWORK,))
await ezsp_f.write_config({})
assert ezsp_f.setConfigurationValue.mock_calls == expected_calls[:-1]


@pytest.mark.parametrize("version", ezsp.EZSP._BY_VERSION)
async def test_config_initialize(version: int, ezsp_f, caplog):
Expand Down

0 comments on commit 29dec53

Please sign in to comment.