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

feat: use yaml to parse action params #447

Merged
merged 4 commits into from
Dec 31, 2024
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
58 changes: 42 additions & 16 deletions custom_components/xiaomi_home/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@
Notify entities for Xiaomi Home.
"""
from __future__ import annotations
import json
import logging
from typing import Optional
from typing import Any, Optional

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.notify import NotifyEntity
from homeassistant.util import yaml
from homeassistant.exceptions import HomeAssistantError

from .miot.miot_spec import MIoTSpecAction
from .miot.miot_device import MIoTDevice, MIoTActionEntity
Expand Down Expand Up @@ -82,6 +83,7 @@ async def async_setup_entry(

class Notify(MIoTActionEntity, NotifyEntity):
"""Notify entities for Xiaomi Home."""
# pylint: disable=unused-argument

def __init__(self, miot_device: MIoTDevice, spec: MIoTSpecAction) -> None:
"""Initialize the Notify."""
Expand All @@ -96,34 +98,58 @@ async def async_send_message(
self, message: str, title: Optional[str] = None
) -> None:
"""Send a message."""
del title
if not message:
_LOGGER.error(
'action exec failed, %s(%s), empty action params',
self.name, self.entity_id)
return
in_list: Any = None
try:
in_list: list = json.loads(message)
except json.JSONDecodeError:
# YAML will convert yes, no, on, off, true, false to the bool type,
# and if it is a string, quotation marks need to be added.
in_list = yaml.parse_yaml(content=message)
except HomeAssistantError:
_LOGGER.error(
'action exec failed, %s(%s), invalid action params format, %s',
self.name, self.entity_id, message)
return

if len(self.spec.in_) == 1 and not isinstance(in_list, list):
in_list = [in_list]
if not isinstance(in_list, list) or len(in_list) != len(self.spec.in_):
_LOGGER.error(
'action exec failed, %s(%s), invalid action params, %s',
self.name, self.entity_id, message)
return

in_value: list[dict] = []
for index, prop in enumerate(self.spec.in_):
if type(in_list[index]).__name__ != prop.format_:
_LOGGER.error(
'action exec failed, %s(%s), invalid params item, '
'which item(%s) in the list must be %s, %s',
self.name, self.entity_id, prop.description_trans,
prop.format_, message)
return
in_value.append({'piid': prop.iid, 'value': in_list[index]})
return await self.action_async(in_list=in_value)
if prop.format_ == 'str':
if isinstance(in_list[index], (bool, int, float, str)):
in_value.append(
{'piid': prop.iid, 'value': str(in_list[index])})
continue
elif prop.format_ == 'bool':
if isinstance(in_list[index], (bool, int)):
# yes, no, on, off, true, false and other bool types
# will also be parsed as 0 and 1 of int.
in_value.append(
{'piid': prop.iid, 'value': bool(in_list[index])})
continue
elif prop.format_ == 'float':
if isinstance(in_list[index], (int, float)):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
continue
elif prop.format_ == 'int':
if isinstance(in_list[index], int):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
continue
# Invalid params type, raise error.
_LOGGER.error(
'action exec failed, %s(%s), invalid params item, '
'which item(%s) in the list must be %s, %s type was %s, %s',
self.name, self.entity_id, prop.description_trans,
prop.format_, in_list[index], type(
in_list[index]).__name__, message)
return
await self.action_async(in_list=in_value)
57 changes: 42 additions & 15 deletions custom_components/xiaomi_home/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@
Text entities for Xiaomi Home.
"""
from __future__ import annotations
import json
import logging
from typing import Optional
from typing import Any, Optional

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.text import TextEntity
from homeassistant.util import yaml
from homeassistant.exceptions import HomeAssistantError

from .miot.const import DOMAIN
from .miot.miot_spec import MIoTSpecAction, MIoTSpecProperty
Expand Down Expand Up @@ -119,16 +120,18 @@ def __init__(self, miot_device: MIoTDevice, spec: MIoTSpecAction) -> None:
async def async_set_value(self, value: str) -> None:
if not value:
return
in_list: list = None
in_list: Any = None
try:
in_list = json.loads(value)
except json.JSONDecodeError as e:
in_list = yaml.parse_yaml(content=value)
except HomeAssistantError as e:
_LOGGER.error(
'action exec failed, %s(%s), invalid action params format, %s',
self.name, self.entity_id, value)
raise ValueError(
f'action exec failed, {self.name}({self.entity_id}), '
f'invalid action params format, {value}') from e
if len(self.spec.in_) == 1 and not isinstance(in_list, list):
in_list = [in_list]
if not isinstance(in_list, list) or len(in_list) != len(self.spec.in_):
_LOGGER.error(
'action exec failed, %s(%s), invalid action params, %s',
Expand All @@ -138,16 +141,40 @@ async def async_set_value(self, value: str) -> None:
f'invalid action params, {value}')
in_value: list[dict] = []
for index, prop in enumerate(self.spec.in_):
if type(in_list[index]).__name__ != prop.format_:
logging.error(
'action exec failed, %s(%s), invalid params item, which '
'item(%s) in the list must be %s, %s', self.name,
self.entity_id, prop.description_trans, prop.format_, value)
raise ValueError(
f'action exec failed, {self.name}({self.entity_id}), '
f'invalid params item, which item({prop.description_trans})'
f' in the list must be {prop.format_}, {value}')
in_value.append({'piid': prop.iid, 'value': in_list[index]})
if prop.format_ == 'str':
if isinstance(in_list[index], (bool, int, float, str)):
in_value.append(
{'piid': prop.iid, 'value': str(in_list[index])})
continue
elif prop.format_ == 'bool':
if isinstance(in_list[index], (bool, int)):
# yes, no, on, off, true, false and other bool types
# will also be parsed as 0 and 1 of int.
in_value.append(
{'piid': prop.iid, 'value': bool(in_list[index])})
continue
elif prop.format_ == 'float':
if isinstance(in_list[index], (int, float)):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
continue
elif prop.format_ == 'int':
if isinstance(in_list[index], int):
in_value.append(
{'piid': prop.iid, 'value': in_list[index]})
continue
# Invalid params type, raise error.
_LOGGER.error(
'action exec failed, %s(%s), invalid params item, '
'which item(%s) in the list must be %s, %s type was %s, %s',
self.name, self.entity_id, prop.description_trans,
prop.format_, in_list[index], type(
in_list[index]).__name__, value)
raise ValueError(
f'action exec failed, {self.name}({self.entity_id}), '
f'invalid params item, which item({prop.description_trans}) '
f'in the list must be {prop.format_}, {in_list[index]} type '
f'was {type(in_list[index]).__name__}, {value}')

self._attr_native_value = value
if await self.action_async(in_list=in_value):
Expand Down
Loading