Skip to content

Commit

Permalink
Merge pull request #100 from mdeweerd/dev
Browse files Browse the repository at this point in the history
Corrected ezsp_backup
  • Loading branch information
mdeweerd authored Oct 30, 2022
2 parents 45eef26 + 3117bf1 commit b366a8b
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 31 deletions.
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ repos:
hooks:
- id: beautysh
- repo: https://github.com/asottile/pyupgrade
rev: v2.38.2
rev: v3.1.0
hooks:
- id: pyupgrade
args:
- --py37-plus
- repo: https://github.com/psf/black
rev: 22.8.0
rev: 22.10.0
hooks:
- id: black
args:
Expand Down Expand Up @@ -110,7 +110,7 @@ repos:
hooks:
- id: isort
- repo: https://github.com/codespell-project/codespell
rev: v2.2.1
rev: v2.2.2
hooks:
- id: codespell
args:
Expand All @@ -128,7 +128,7 @@ repos:
- --py-version=3.7
# exclude: ^$
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.981
rev: v0.982
hooks:
- id: mypy
args:
Expand Down
36 changes: 17 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,13 @@ test the service first before adding them to an automation.
Go to Developer Tools > Services in your instance :
[![Open your Home Assistant instance and show your service developer tools.](https://my.home-assistant.io/badges/developer_services.svg)](https://my.home-assistant.io/redirect/developer_services/).

Choose `zha_toolkit.execute` as the service.\
Most parameters can be set
using the UI, there are some cases where you may want to enable Yaml entry
\- you'll have some more flexibility and all parameters fit in your browser
view. On the other hand, the UI interface makes it easier to select the
entity. You can switch back and forth!
Choose the generic service `zha_toolkit.execute` or - more convenient - the
specific `zha_toolkit.<COMMAND>` as the service.\
Most parameters can be
set using the UI, there are some cases where you may want to enable Yaml
entry - you'll have some more flexibility and all parameters fit in your
browser view. On the other hand, the UI interface makes it easier to select
the entity. You can switch back and forth!

There are several examples below for different commands. You can copy/paste
them to start from.
Expand Down Expand Up @@ -251,11 +252,15 @@ service: zha_toolkit.SOME_SERVICE
# Valid possibilities for the `ieee` address
# The full IEEE address:
ieee: 00:12:4b:00:24:42:d1:dc
```
```yaml
service: zha_toolkit.SOME_SERVICE
# The short network address
ieee: 0x2F3E
```
```yaml
service: zha_toolkit.SOME_SERVICE
# entity name (one of them)
ieee: light.tz3000_odygigth_ts0505a_12c90efe_level_light_color_on_off
Expand Down Expand Up @@ -498,9 +503,8 @@ logging is active, this will be visible in the `home_assistant.log`. The
last read this can be written to a state.

```yaml
service: zha_toolkit.execute
service: zha_toolkit.attr_write
data:
command: attr_write
ieee: 5c:02:72:ff:fe:92:c2:5d
# The endpoint is optional - when missing tries to find endpoint matching the cluster
endpoint: 11
Expand Down Expand Up @@ -808,10 +812,9 @@ The result is also added to the event data in the event\['data'\]\['scan'\]
field

```yaml
service: zha_toolkit.execute
service: zha_toolkit.scan_device
data:
ieee: 00:12:4b:00:22:08:ed:1a
command: scan_device
# Optional: endpoint to scan, when missing: all known endpoints
# endpoint: 1
# Optional: endpoints to scan, when missing: all known endpoints
Expand All @@ -821,9 +824,8 @@ data:
Scan using the entity name:

```yaml
service: zha_toolkit.execute
service: zha_toolkit.scan_device
data:
command: scan_device
ieee: light.tz3000_odygigth_ts0505a_12c90efe_level_light_color_on_off
```

Expand Down Expand Up @@ -927,12 +929,10 @@ than the core that has though release procedures and is not as easily
modifiable as a `custom_component`.

```yaml
service: zha_toolkit.execute
service: zha_toolkit.zcl_cmd
data:
# Device IEEE address - mandatory
ieee: 5c:02:72:ff:fe:92:c2:5d
# Service command - mandatory
command: zcl_cmd
# Command id - mandatory
cmd: 0
# Cluster id - mandatory
Expand All @@ -955,10 +955,9 @@ data:
### `zcl_cmd` Example: Send `on` command to an OnOff Cluster.

```yaml
service: zha_toolkit.execute
service: zha_toolkit.zcl_cmd
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
command: zcl_cmd
cmd: 1
cluster: 6
endpoint: 11
Expand Down Expand Up @@ -1203,9 +1202,8 @@ You can use the blueprint to setup daily backup:
The name of that backup is according to the format

```yaml
service: zha_toolkit.execute
service: zha_toolkit.ezsp_backup
data:
command: ezsp_backup
# Optional command_data, string added to the basename.
# With this example the backup is written to `nwk_backup_20220105.json`
command_data: _20220105
Expand Down
1 change: 1 addition & 0 deletions STATS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Badges showing number of downloads per version

- ![badge latest](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/latest/total.svg)
- ![badge v0.8.20](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v0.8.20/total.svg)
- ![badge v0.8.19](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v0.8.19/total.svg)
- ![badge v0.8.18](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v0.8.18/total.svg)
- ![badge v0.8.17](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v0.8.17/total.svg)
Expand Down
81 changes: 77 additions & 4 deletions custom_components/zha_toolkit/binds.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,26 @@ async def bind_group(
group_id = u.str2int(data)
zdo = src_dev.zdo
src_out_cls = BINDABLE_OUT_CLUSTERS
src_in_cls = BINDABLE_IN_CLUSTERS

# find src ep_id
dst_addr = MultiAddress()
dst_addr.addrmode = t.uint8_t(1)
dst_addr.nwk = t.uint16_t(group_id)
u_epid = params[p.EP_ID]
u_cluster_id = params[p.CLUSTER_ID]

if u_cluster_id is not None:
src_out_cls = [u_cluster_id]
src_in_cls = [u_cluster_id]

results: dict[int, list[dict[str, int]]] = {}
for src_out_cluster in src_out_cls:
src_epid = None
for ep_id, ep in src_dev.endpoints.items():
if u_epid is not None and ep_id != u_epid:
# Endpoint not selected
continue
if ep_id == 0:
continue
if src_out_cluster in ep.out_clusters:
Expand Down Expand Up @@ -77,9 +88,61 @@ async def bind_group(
bind_result["result"] = res
results[src_epid].append(bind_result)
LOGGER.debug(
"0x%04x: binding group 0x%04x: %s", src_dev.nwk, group_id, res
"0x%04x/0x%02x/0x%04x(OUT): binding group 0x%04x: %s",
src_dev.nwk,
src_epid,
src_out_cluster,
group_id,
res,
)

# find src ep_id
dst_addr = MultiAddress()
dst_addr.addrmode = t.uint8_t(1)
dst_addr.nwk = t.uint16_t(group_id)

for src_in_cluster in src_in_cls:
src_epid = None
for ep_id, ep in src_dev.endpoints.items():
if u_epid is not None and ep_id != u_epid:
# Endpoint not selected
continue
if ep_id == 0:
continue
if src_in_cluster in ep.in_clusters:
src_epid = ep_id
break
if not src_epid:
LOGGER.debug(
"0x%04x: skipping %s cluster (not present)",
src_dev.nwk,
src_in_cluster,
)
continue
if src_epid not in results:
results[src_epid] = []
LOGGER.debug(
"0x%04x: binding %s, ep: %s, cluster: %s(IN)",
src_dev.nwk,
str(src_dev.ieee),
src_epid,
src_in_cluster,
)
bind_result = {"endpoint_id": src_epid, "cluster_id": src_in_cluster}

res = await zdo.request(
ZDOCmd.Bind_req, src_dev.ieee, src_epid, src_in_cluster, dst_addr
)
bind_result["result"] = res
results[src_epid].append(bind_result)
LOGGER.debug(
"0x%04x/0x%02x/0x%04x(IN): binding group 0x%04x: %s",
src_dev.nwk,
src_epid,
src_in_cluster,
group_id,
res,
)
event_data["result"] = results


Expand Down Expand Up @@ -158,6 +221,12 @@ async def bind_ieee(
src_out_clusters = BINDABLE_OUT_CLUSTERS
src_in_clusters = BINDABLE_IN_CLUSTERS

u_epid = params[p.EP_ID]
u_cluster_id = params[p.CLUSTER_ID]
if u_cluster_id is not None:
src_out_clusters = [u_cluster_id]
src_in_clusters = [u_cluster_id]

# TODO: Filter according to params[p.CLUSTER_ID]

results: dict[int, dict] = {}
Expand All @@ -166,7 +235,9 @@ async def bind_ieee(
src_endpoints = [
ep_id
for ep_id, ep in src_dev.endpoints.items()
if ep_id != 0 and src_out_cluster in ep.out_clusters
if ep_id != 0
and src_out_cluster in ep.out_clusters
and (u_epid is None or u_epid == ep_id)
]
LOGGER.debug(
"0x%04x: got the %s endpoints for %s cluster",
Expand Down Expand Up @@ -226,7 +297,9 @@ async def bind_ieee(
src_endpoints = [
ep_id
for ep_id, ep in src_dev.endpoints.items()
if ep_id != 0 and src_in_cluster in ep.in_clusters
if ep_id != 0
and src_in_cluster in ep.in_clusters
and (u_epid is None or u_epid == ep_id)
]
LOGGER.debug(
"0x%04x: got the %s endpoints for %s cluster",
Expand Down Expand Up @@ -352,7 +425,7 @@ async def binds_remove_all(
addr_mode = binding["dst"]["addrmode"]

res = None
# Note, the code belowe is essentally two times the same
# Note, the code belowe is essentially two times the same
# but the goal is to make a distinciont between group
# and ieee addressing for testing/evolutions.
if addr_mode == 1:
Expand Down
28 changes: 26 additions & 2 deletions custom_components/zha_toolkit/ezsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,15 @@ async def ezsp_backup_legacy(
jsonfile.write(json.dumps(result, indent=4))


async def ezsp_dummy_networkInit():
return (bellows.types.EmberStatus.SUCCESS,)


async def ezsp_click_get_echo(s):
LOGGER.error(f"GET_ECHO: {s}")
bellows.cli._result = s


async def ezsp_backup(
app, listener, ieee, cmd, data, service, params, event_data
):
Expand All @@ -321,11 +330,26 @@ async def ezsp_backup(
raise Exception(msg)

# Import stuff we need
import io
import json
from contextlib import redirect_stdout

from bellows.cli import backup as bellows_backup

result = await bellows_backup._backup(app._ezsp)
# from pkg_resources import parse_version
# bellows_version = bellows.__version__
# use_click = (parse_version(bellows_version)>parse_version("0.3.1"))

try:
# Network is already initialised, fake result for backup function
org_network_init = app._ezsp.networkInit
app._ezsp.networkInit = ezsp_dummy_networkInit
f = io.StringIO()
with redirect_stdout(f):
await bellows_backup._backup(app._ezsp)
result = f.getvalue()
finally:
app._ezsp.networkInit = org_network_init # pylint: disable=E0601

# Store backup information to file

Expand All @@ -339,4 +363,4 @@ async def ezsp_backup(
fname = out_dir + "nwk_backup" + str(data) + ".json"

with open(fname, "w", encoding="utf_8") as jsonfile:
jsonfile.write(json.dumps(result, indent=4))
jsonfile.write(json.dumps(json.loads(result), indent=4))
Loading

0 comments on commit b366a8b

Please sign in to comment.