Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Energy scan #283

Merged
merged 7 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ repos:
additional_dependencies:
- mdformat-toc
- mdformat-beautysh
- setuptools # workaround for beautysh
# -mdformat-shfmt
# -mdformat-tables
- mdformat-config
Expand Down Expand Up @@ -68,6 +69,9 @@ repos:
rev: v6.2.1
hooks:
- id: beautysh
additional_dependencies:
- setuptools # workaround for beautysh

- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
hooks:
Expand Down
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ ZHA Toolkit can also:
- [`zha_devices`: Device Information to Event or CSV or Script variable](#zha_devices-device-information-to-event-or-csv-or-script-variable)
- [`register_services`: Reregister ZHA-Toolkit services](#register_services-reregister-zha-toolkit-services)
- [`ha_set_state` - Update HA state](#ha_set_state---update-ha-state)
- [`misc_energy_scan`: Perform an energy scan](#misc_energy_scan-perform-an-energy-scan)
- [User method](#user-method)
- [Manufacturers](#manufacturers)
- [Tuya](#tuya)
Expand Down Expand Up @@ -1921,6 +1922,49 @@ service code is entered which is incompatible with this functionality.
This is not strictly a `zha` specific tool, but useful in some scripting
situations.

### `misc_energy_scan`: Perform an energy scan

Scan Zigbee channels for congestion level. The value is a percentage from
zero to 100. A lower value is less congested.

```yaml
service: zha_toolkit.misc_energy_scan
data:
# Optional: CSV file to write attribute to - located in /config/csv/...
csvout: energy_scan.csv
```

The values can vary quite a bit between scans. You can create helpers to
store results which will allow you to see trends via the History tab. This
automation runs each hour:

```
- id: zigbee_energy_scan
alias: Zigbee Energy Scan
mode: single
triggers:
- trigger: time_pattern
# Matches every hour at 17 minutes past the hour
minutes: 17
actions:
- action: zha_toolkit.execute
data:
command: misc_energy_scan
response_variable: scan
- repeat:
for_each: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
sequence:
- action: input_number.set_value
target:
entity_id: "input_number.zigbee_energy_channel_{{ repeat.item }}"
data:
value: "{{ scan['energy_scan'][repeat.item] | round }}"
```

Creating 16 input_number helpers can be tedious. ZHA recommends only
channels 15, 20, and 25 be used. Alternatively you can create just three
helpers and reduce the for_each list to only those three channels.

## User method

You can add your own Python commands in `local/user.py`. Your file is
Expand Down
36 changes: 36 additions & 0 deletions custom_components/zha_toolkit/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,39 @@ async def misc_settime(
except DeliveryError as e:
event_data["success"] = False
event_data["msg"] = f"{e!r}"


async def misc_energy_scan(
app, listener, ieee, cmd, data, service, params, event_data
):
"""Run energy scan for each channel."""
# See https://github.com/zigpy/zigpy-cli/blob/dev/README.md#performing-an-energy-scan # noqa: E501
# Lower value means less congestion.

LOGGER.debug("Energy scan.")
scan = await app.energy_scan(
channels=t.Channels.ALL_CHANNELS, duration_exp=4, count=1
)
event_data["energy_scan"] = {
channel: 100 * energy / 255 for channel, energy in scan.items()
}

if params[p.CSV_FILE] is not None:
# write CSV header
u.append_to_csvfile(
["channel", "energy"],
"csv",
params[p.CSV_FILE],
"Energy Scan",
listener=listener,
overwrite=True,
)
# write CSV data
for channel, energy in scan.items():
u.append_to_csvfile(
[channel, 100 * energy / 255],
"csv",
params[p.CSV_FILE],
str(channel),
listener=listener,
)
Loading