Skip to content

Commit 8f3db79

Browse files
committed
Merge branch 'dev' of github.com:mdeweerd/zha-toolkit into dev
2 parents c6d00c8 + 0ed1989 commit 8f3db79

File tree

9 files changed

+308
-24
lines changed

9 files changed

+308
-24
lines changed

.pre-commit-config.yaml

+4-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ repos:
6969
hooks:
7070
- id: beautysh
7171
- repo: https://github.com/shellcheck-py/shellcheck-py
72-
rev: v0.9.0.6
72+
rev: v0.10.0.1
7373
hooks:
7474
- id: shellcheck
7575
files: ^[^\.].*\.sh$
@@ -152,7 +152,7 @@ repos:
152152
# - homeassistant-stubs>=2023.1.7
153153
# exclude: ^$
154154
- repo: https://github.com/pre-commit/mirrors-mypy
155-
rev: v1.5.1
155+
rev: v1.14.0
156156
hooks:
157157
- id: mypy
158158
args:
@@ -166,7 +166,8 @@ repos:
166166
- --show-error-context
167167
additional_dependencies:
168168
- zigpy==0.61.0
169-
- aiofiles>=0.4.0
169+
- types_aiofiles>=0.4.0
170+
- types_pytz>=2023.1
170171
# - cryptography==3.3.2 # Compatible/Available on cygwin
171172
#- homeassistant-stubs>=2023.1.7
172173
#- pydantic

STATS.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Badges showing number of downloads per version
22

33
- ![badge latest](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/latest/total.svg)
4+
- ![badge v1.1.24](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v1.1.24/total.svg)
5+
- ![badge v1.1.23](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v1.1.23/total.svg)
6+
- ![badge v1.1.22](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v1.1.22/total.svg)
47
- ![badge v1.1.21](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v1.1.21/total.svg)
58
- ![badge v1.1.20](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v1.1.20/total.svg)
69
- ![badge v1.1.19](https://img.shields.io/github/downloads/mdeweerd/zha-toolkit/v1.1.19/total.svg)

custom_components/zha_toolkit/__init__.py

+52-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import homeassistant.helpers.config_validation as cv
66
import voluptuous as vol
7+
from homeassistant.config_entries import ConfigEntry
78

89
try:
910
from homeassistant.components.zha import Gateway as ZHAGateway
@@ -14,12 +15,13 @@
1415
from zigpy import types as t
1516
from zigpy.exceptions import DeliveryError
1617

18+
from . import const as c
1719
from . import params as PARDEFS
1820
from . import utils as u
1921

20-
DEPENDENCIES = ["zha"]
22+
DOMAIN = c.DOMAIN
2123

22-
DOMAIN = "zha_toolkit"
24+
DEPENDENCIES = ["zha"]
2325

2426
# Legacy parameters
2527
ATTR_COMMAND = "command"
@@ -935,3 +937,51 @@ async def command_handler_register_services(
935937
app, listener, ieee, cmd, data, service, params, event_data
936938
):
937939
await _register_services(u.get_hass(listener))
940+
941+
942+
# For migrating from one version to another.
943+
# Added to migrate from a configuration.yaml entry to UI configuration
944+
# Example migration function
945+
# Return True when migration is successful
946+
#
947+
async def async_migrate_entry(hass, config_entry: ConfigEntry):
948+
"""Migrate old entry."""
949+
LOGGER.debug(
950+
"Migrating configuration from version %s.%s",
951+
config_entry.version,
952+
config_entry.minor_version,
953+
)
954+
955+
from .config_flow import ZhaToolkitCustomConfigFlow as CF
956+
957+
new_data = {**config_entry.data}
958+
959+
# Sample code to test version migration and update it
960+
# if config_entry.version > 1:
961+
# # This means the user has downgraded from a future version
962+
# return False
963+
964+
# if config_entry.version == 1:
965+
966+
# new_data = {**config_entry.data}
967+
# if config_entry.minor_version < 2:
968+
# # TODO: modify Config Entry data with changes in version 1.2
969+
# pass
970+
# if config_entry.minor_version < 3:
971+
# # TODO: modify Config Entry data with changes in version 1.3
972+
# pass
973+
974+
hass.config_entries.async_update_entry(
975+
config_entry,
976+
data=new_data,
977+
minor_version=CF.MIN_VERSION,
978+
version=CF.VERSION,
979+
)
980+
981+
LOGGER.debug(
982+
"Migration to configuration version %s.%s successful",
983+
config_entry.version,
984+
config_entry.minor_version,
985+
)
986+
987+
return True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Inspired from https://aarongodfrey.dev/home%20automation
2+
# /building_a_home_assistant_custom_component_part_3/
3+
# from copy import deepcopy
4+
import logging
5+
from typing import Any, Optional
6+
7+
from homeassistant import config_entries
8+
9+
from . import utils as u
10+
from .const import DOMAIN
11+
12+
# from homeassistant.const import CONF_ACCESS_TOKEN,CONF_NAME
13+
# from homeassistant.const import CONF_PATH,CONF_URL
14+
# from homeassistant.core import callback
15+
# from homeassistant.helpers.aiohttp_client import async_get_clientsession
16+
# import homeassistant.helpers.config_validation as cv
17+
# from homeassistant.helpers.entity_registry import (
18+
# async_entries_for_config_entry,
19+
# async_get_registry,
20+
# )
21+
# import voluptuous as vol
22+
23+
24+
_LOGGER = logging.getLogger(__name__)
25+
26+
# INITIAL_CONFIG_SCHEMA = vol.Schema(
27+
# #{vol.Required(CONF_SKEY): cv.string, vol.Optional(CONF_O_KEY): cv.string}
28+
# )
29+
# EXTRA_CONF_SCHEMA = vol.Schema(
30+
# {
31+
# #vol.Required(CONF_PATH): cv.string,
32+
# #vol.Optional(CONF_NAME): cv.string,
33+
# #vol.Optional("add_another"): cv.boolean,
34+
# }
35+
# )
36+
37+
# OPTIONS_SCHEMA = vol.Schema({vol.Optional(CONF_NM, default="go"): cv.string})
38+
39+
40+
class ZhaToolkitCustomConfigFlow(
41+
config_entries.ConfigFlow, domain=DOMAIN
42+
): # type:ignore[call-arg]
43+
"""Zha Toolkit Custom config flow."""
44+
45+
VERSION = 0
46+
MINOR_VERSION = 1
47+
48+
data: Optional[dict[str, Any]]
49+
50+
async def my_async_create_entry(self):
51+
if self.data is None:
52+
self.data = {}
53+
self.data["VERSION"] = await u.getVersion()
54+
# Create the configuration entry
55+
return self.async_create_entry(title="ZHA Toolkit", data=self.data)
56+
57+
async def async_step_user(
58+
self, user_input: Optional[dict[str, Any]] = None
59+
):
60+
"""Invoked when a user initiates a flow via the user interface."""
61+
# errors: dict[str, str] = {}
62+
# Nothing special to configure, end configuration step
63+
return self.my_async_create_entry()
64+
65+
66+
# if user_input is not None:
67+
# # Initially None, but not None when user entered data.
68+
# try:
69+
# await validate_something(
70+
# user_input[CONF_ACCESS_TOKEN], self.hass
71+
# )
72+
# except ValueError:
73+
# errors["base"] = "error_message" # key in `strings.json`
74+
# if not errors:
75+
# # Input is valid, set data.
76+
# self.data = user_input
77+
# self.data[CONF_SOME_KEY] = []
78+
# # Return the form of the next step.
79+
# return await self.async_step_repo()
80+
81+
# return self.async_show_form(
82+
# step_id="user", data_schema=INITIAL_CONFIG_SCHEMA, errors=errors
83+
# )
84+
85+
# async def async_step_repo(
86+
# self, user_input: Optional[Dict[str, Any]] = None
87+
# ):
88+
# """Second step in config flow to add a repo to watch."""
89+
# errors: Dict[str, str] = {}
90+
# if user_input is not None:
91+
# # Validate the path.
92+
# try:
93+
# await validate_path(
94+
# user_input[CONF_PATH],
95+
# self.data[CONF_ACCESS_TOKEN],
96+
# self.hass,
97+
# )
98+
# except ValueError:
99+
# errors["base"] = "invalid_path"
100+
101+
# if not errors:
102+
# # Input is valid, set data.
103+
# self.data[CONF_REPOS].append(
104+
# {
105+
# "path": user_input[CONF_PATH],
106+
# "name": user_input.get(
107+
# CONF_NAME, user_input[CONF_PATH]
108+
# ),
109+
# }
110+
# )
111+
# # If user ticked the box show this form again so they can add
112+
# # an additional repo.
113+
# if user_input.get("add_another", False):
114+
# return await self.async_step_repo()
115+
116+
# # User is done adding repos, create the config entry.
117+
# return self.async_create_entry(
118+
# title="GitHub Custom", data=self.data
119+
# )
120+
121+
# return self.async_show_form(
122+
# step_id="repo", data_schema=EXTRA_CONF_SCHEMA, errors=errors
123+
# )
124+
125+
# @staticmethod
126+
# @callback
127+
# def async_get_options_flow(config_entry):
128+
# """Get the options flow for this handler."""
129+
# return OptionsFlowHandler(config_entry)
130+
131+
132+
# class OptionsFlowHandler(config_entries.OptionsFlow):
133+
# """Handles options flow for the component."""
134+
135+
# def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
136+
# self.config_entry = config_entry
137+
138+
# async def async_step_init(
139+
# self, user_input: Dict[str, Any] = None
140+
# ) -> Dict[str, Any]:
141+
# """Manage the options for the custom component."""
142+
# errors: Dict[str, str] = {}
143+
# # Grab all configured repos from the entity registry so we can populate
144+
# # the multi-select dropdown that will allow a user to remove a repo.
145+
# entity_registry = await async_get_registry(self.hass)
146+
# entries = async_entries_for_config_entry(
147+
# entity_registry, self.config_entry.entry_id
148+
# )
149+
# # Default value for our multi-select.
150+
# all_repos = {e.entity_id: e.original_name for e in entries}
151+
# repo_map = {e.entity_id: e for e in entries}
152+
153+
# if user_input is not None:
154+
# updated_repos = deepcopy(self.config_entry.data[CONF_REPOS])
155+
156+
# # Remove any unchecked repos.
157+
# removed_entities = [
158+
# entity_id
159+
# for entity_id in repo_map.keys()
160+
# if entity_id not in user_input["repos"]
161+
# ]
162+
# for entity_id in removed_entities:
163+
# # Unregister from HA
164+
# entity_registry.async_remove(entity_id)
165+
# # Remove from our configured repos.
166+
# entry = repo_map[entity_id]
167+
# entry_path = entry.unique_id
168+
# updated_repos = [
169+
# e for e in updated_repos if e["path"] != entry_path
170+
# ]
171+
172+
# if user_input.get(CONF_PATH):
173+
# # Validate the path.
174+
# access_token = self.hass.data[DOMAIN][
175+
# self.config_entry.entry_id
176+
# ][CONF_ACCESS_TOKEN]
177+
# try:
178+
# await validate_path(
179+
# user_input[CONF_PATH], access_token, self.hass
180+
# )
181+
# except ValueError:
182+
# errors["base"] = "invalid_path"
183+
184+
# if not errors:
185+
# # Add the new repo.
186+
# updated_repos.append(
187+
# {
188+
# "path": user_input[CONF_PATH],
189+
# "name": user_input.get(
190+
# CONF_NAME, user_input[CONF_PATH]
191+
# ),
192+
# }
193+
# )
194+
195+
# if not errors:
196+
# # Value of data will be set on the options property of our
197+
# # config_entry instance.
198+
# return self.async_create_entry(
199+
# title="",
200+
# data={CONF_REPOS: updated_repos},
201+
# )
202+
203+
# options_schema = vol.Schema(
204+
# {
205+
# vol.Optional(
206+
# "repos", default=list(all_repos.keys())
207+
# ): cv.multi_select(all_repos),
208+
# vol.Optional(CONF_PATH): cv.string,
209+
# vol.Optional(CONF_NAME): cv.string,
210+
# }
211+
# )
212+
# return self.async_show_form(
213+
# step_id="init", data_schema=options_schema, errors=errors
214+
# )

custom_components/zha_toolkit/utils.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@
5858
MANIFEST: dict[str, str | list[str]] = {}
5959

6060

61-
def get_zha_gateway(hass: HomeAssistant) -> ZHAGateway | zha_helpers.ZHAGatewayProxy:
61+
def get_zha_gateway(
62+
hass: HomeAssistant,
63+
) -> ZHAGateway | zha_helpers.ZHAGatewayProxy:
6264
"""Get the ZHA gateway object."""
6365
if parse_version(HA_VERSION) >= parse_version("2024.8"):
6466
return zha_helpers.get_zha_gateway(hass)
@@ -68,12 +70,13 @@ def get_zha_gateway(hass: HomeAssistant) -> ZHAGateway | zha_helpers.ZHAGatewayP
6870

6971

7072
def get_zha_devices(listener: ZHAGateway | zha_helpers.ZHAGatewayProxy):
71-
devices = getattr(listener, "device_proxies", None)
72-
if devices is None:
73-
# Old method
74-
devices = getattr(listener, "devices", None)
75-
if devices is not None:
76-
return devices.values()
73+
devices = getattr(listener, "device_proxies", None)
74+
if devices is None:
75+
# Old method
76+
devices = getattr(listener, "devices", None)
77+
if devices is not None:
78+
return devices.values()
79+
return []
7780

7881

7982
def get_zha_gateway_hass(

custom_components/zha_toolkit/zcl_attr.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -548,9 +548,12 @@ async def attr_write( # noqa: C901
548548

549549
# Write value to provided state or state attribute
550550
if params[p.STATE_ID] is not None:
551-
if len(result_read[1]) == 0 and len(result_read[0]) == 1:
551+
if (
552+
len(result_read[1]) == 0 # type:ignore[index]
553+
and len(result_read[0]) == 1 # type:ignore[index]
554+
):
552555
# No error and one result
553-
for attr_id, val in result_read[0].items():
556+
for attr_id, val in result_read[0].items(): # type:ignore[index]
554557
if state_template_str is not None:
555558
if val is None:
556559
LOGGER.debug(

0 commit comments

Comments
 (0)