From e5074e2191cca944c9e846c4b75f1b9b4ffd77b7 Mon Sep 17 00:00:00 2001 From: Xavier Moreno Date: Sat, 9 Jan 2021 17:24:05 +0100 Subject: [PATCH] feat(action-type): add new concept of action types to run different type of actions related to #173 --- Pipfile | 1 + apps/controllerx/cx_const.py | 13 +- .../cx_core/action_type/__init__.py | 46 ++++ apps/controllerx/cx_core/action_type/base.py | 25 +++ .../action_type/call_service_action_type.py | 28 +++ .../cx_core/action_type/delay_action_type.py | 17 ++ .../action_type/predefined_action_type.py | 39 ++++ .../cx_core/action_type/scene_action_type.py | 17 ++ apps/controllerx/cx_core/color_helper.py | 2 +- apps/controllerx/cx_core/controller.py | 186 +++++++---------- .../cx_core/integration/__init__.py | 4 +- .../controllerx/cx_core/integration/deconz.py | 7 +- apps/controllerx/cx_core/integration/mqtt.py | 4 +- apps/controllerx/cx_core/integration/state.py | 4 +- apps/controllerx/cx_core/integration/z2m.py | 4 +- apps/controllerx/cx_core/integration/zha.py | 4 +- .../cx_core/type/cover_controller.py | 8 +- .../cx_core/type/light_controller.py | 196 +++++++++++------- .../cx_core/type/media_player_controller.py | 12 +- .../cx_core/type/switch_controller.py | 4 +- apps/controllerx/cx_devices/aqara.py | 44 ++-- apps/controllerx/cx_devices/ikea.py | 94 ++++----- apps/controllerx/cx_devices/legrand.py | 6 +- apps/controllerx/cx_devices/livarno.py | 4 +- apps/controllerx/cx_devices/lutron.py | 16 +- apps/controllerx/cx_devices/muller_licht.py | 12 +- apps/controllerx/cx_devices/osram.py | 4 +- apps/controllerx/cx_devices/phillips.py | 10 +- apps/controllerx/cx_devices/smartthings.py | 14 +- apps/controllerx/cx_devices/sonoff.py | 4 +- apps/controllerx/cx_devices/terncy.py | 6 +- apps/controllerx/cx_devices/trust.py | 6 +- setup.cfg | 1 + tests/conftest.py | 15 +- .../action-types/arrow_left_click_test.yaml | 8 + .../arrow_left_double_click_test.yaml | 8 + .../action-types/arrow_right_click_test.yaml | 9 + .../brightness_down_click_test.yaml | 15 ++ .../brightness_up_click_test.yaml | 8 + tests/integ_tests/action-types/config.yaml | 28 +++ .../integ_tests/action-types/toggle_test.yaml | 8 + tests/integ_tests/integ_test.py | 6 +- tests/test_utils.py | 4 +- .../predefined_action_type_test.py | 21 ++ tests/unit_tests/cx_core/controller_test.py | 91 ++++---- .../cx_core/custom_controller_test.py | 8 +- tests/unit_tests/cx_core/type/type_test.py | 9 +- tests/unit_tests/cx_devices/devices_test.py | 10 +- 48 files changed, 703 insertions(+), 387 deletions(-) create mode 100644 apps/controllerx/cx_core/action_type/__init__.py create mode 100644 apps/controllerx/cx_core/action_type/base.py create mode 100644 apps/controllerx/cx_core/action_type/call_service_action_type.py create mode 100644 apps/controllerx/cx_core/action_type/delay_action_type.py create mode 100644 apps/controllerx/cx_core/action_type/predefined_action_type.py create mode 100644 apps/controllerx/cx_core/action_type/scene_action_type.py create mode 100644 tests/integ_tests/action-types/arrow_left_click_test.yaml create mode 100644 tests/integ_tests/action-types/arrow_left_double_click_test.yaml create mode 100644 tests/integ_tests/action-types/arrow_right_click_test.yaml create mode 100644 tests/integ_tests/action-types/brightness_down_click_test.yaml create mode 100644 tests/integ_tests/action-types/brightness_up_click_test.yaml create mode 100644 tests/integ_tests/action-types/config.yaml create mode 100644 tests/integ_tests/action-types/toggle_test.yaml create mode 100644 tests/unit_tests/cx_core/action-types/predefined_action_type_test.py diff --git a/Pipfile b/Pipfile index e9f45d06..1b45cef5 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,7 @@ pytest = "==6.2.1" pytest-asyncio = "==0.14.0" pytest-cov = "==2.10.1" pytest-mock = "==3.5.0" +pytest-timeout = "==1.4.2" mock = "==4.0.3" pre-commit = "==2.9.3" commitizen = "==2.13.0" diff --git a/apps/controllerx/cx_const.py b/apps/controllerx/cx_const.py index 372ecf76..0872b4f3 100644 --- a/apps/controllerx/cx_const.py +++ b/apps/controllerx/cx_const.py @@ -1,10 +1,15 @@ -from typing import Any, Awaitable, Callable, Dict, Tuple, Union +from typing import Any, Awaitable, Callable, Dict, List, Mapping, Tuple, Union ActionFunction = Callable[..., Awaitable[Any]] -TypeAction = Union[ActionFunction, Tuple[Any, ...], str] +ActionFunctionWithParams = Tuple[ActionFunction, Tuple] +TypeAction = Union[ActionFunction, ActionFunctionWithParams] ActionEvent = Union[str, int] -TypeActionsMapping = Dict[ActionEvent, TypeAction] -ActionsMapping = Dict[ActionEvent, Union[ActionFunction, Tuple[Any, ...]]] +PredefinedActionsMapping = Dict[str, TypeAction] +DefaultActionsMapping = Mapping[ActionEvent, str] + +CustomAction = Union[str, Dict[str, Any]] +CustomActions = Union[List[CustomAction], CustomAction] +CustomActionsMapping = Mapping[ActionEvent, CustomActions] class Light: diff --git a/apps/controllerx/cx_core/action_type/__init__.py b/apps/controllerx/cx_core/action_type/__init__.py new file mode 100644 index 00000000..7169a314 --- /dev/null +++ b/apps/controllerx/cx_core/action_type/__init__.py @@ -0,0 +1,46 @@ +from typing import TYPE_CHECKING, Dict, List, Type + +from cx_const import ActionEvent, CustomAction, CustomActions +from cx_core.action_type.base import ActionType +from cx_core.action_type.call_service_action_type import CallServiceActionType +from cx_core.action_type.delay_action_type import DelayActionType +from cx_core.action_type.predefined_action_type import PredefinedActionType +from cx_core.action_type.scene_action_type import SceneActionType + +if TYPE_CHECKING: + from cx_core import Controller + +ActionsMapping = Dict[ActionEvent, List[ActionType]] + +action_type_mapping: Dict[str, Type[ActionType]] = { + "action": PredefinedActionType, + "service": CallServiceActionType, + "scene": SceneActionType, + "delay": DelayActionType, +} + + +def parse_actions(controller: "Controller", data: CustomActions) -> List[ActionType]: + actions: CustomActions + if isinstance(data, (list, tuple)): + actions = list(data) + else: + actions = [data] + + return [_parse_action(controller, action) for action in actions] + + +def _parse_action(controller: "Controller", action: CustomAction) -> ActionType: + if isinstance(action, str): + return PredefinedActionType(controller, {"action": action}) + try: + return next( + action_type(controller, action) + for key in action + for action_type_key, action_type in action_type_mapping.items() + if key == action_type_key + ) + except StopIteration: + raise ValueError( + f"Not able to parse `{action}`. Available keys are: {action_type_mapping.keys()}" + ) diff --git a/apps/controllerx/cx_core/action_type/base.py b/apps/controllerx/cx_core/action_type/base.py new file mode 100644 index 00000000..bac723ac --- /dev/null +++ b/apps/controllerx/cx_core/action_type/base.py @@ -0,0 +1,25 @@ +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, Dict, Optional + +from cx_core.integration import EventData + +if TYPE_CHECKING: + from cx_core import Controller + + +class ActionType(ABC): + controller: "Controller" + + def __init__(self, controller: "Controller", action: Dict[str, Any]) -> None: + self.controller = controller + self.initialize(**action) + + def initialize(self, **kwargs) -> None: + pass + + @abstractmethod + async def run(self, extra: Optional[EventData] = None) -> None: + raise NotImplementedError + + def __str__(self) -> str: + return f"{self.__class__.__name__}" diff --git a/apps/controllerx/cx_core/action_type/call_service_action_type.py b/apps/controllerx/cx_core/action_type/call_service_action_type.py new file mode 100644 index 00000000..c2210fe5 --- /dev/null +++ b/apps/controllerx/cx_core/action_type/call_service_action_type.py @@ -0,0 +1,28 @@ +from typing import Any, Dict, Optional + +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + + +class CallServiceActionType(ActionType): + service: str + entity_id: Optional[str] + data: Dict[str, Any] + + def initialize(self, **kwargs) -> None: + self.service = kwargs["service"] + self.entity_id = kwargs.get("entity_id") + self.data = kwargs.get("data", {}) + + async def run(self, extra: Optional[EventData] = None) -> None: + if self.entity_id: + if "entity_id" in self.data: + del self.data["entity_id"] + await self.controller.call_service( + self.service, entity_id=self.entity_id, **self.data + ) + else: + await self.controller.call_service(self.service, **self.data) + + def __str__(self) -> str: + return f"Service ({self.service})" diff --git a/apps/controllerx/cx_core/action_type/delay_action_type.py b/apps/controllerx/cx_core/action_type/delay_action_type.py new file mode 100644 index 00000000..5515c90b --- /dev/null +++ b/apps/controllerx/cx_core/action_type/delay_action_type.py @@ -0,0 +1,17 @@ +from typing import Optional + +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + + +class DelayActionType(ActionType): + delay: int + + def initialize(self, **kwargs) -> None: + self.delay = kwargs["delay"] + + async def run(self, extra: Optional[EventData] = None) -> None: + await self.controller.sleep(self.delay) + + def __str__(self) -> str: + return f"Delay ({self.delay} seconds)" diff --git a/apps/controllerx/cx_core/action_type/predefined_action_type.py b/apps/controllerx/cx_core/action_type/predefined_action_type.py new file mode 100644 index 00000000..993aefe8 --- /dev/null +++ b/apps/controllerx/cx_core/action_type/predefined_action_type.py @@ -0,0 +1,39 @@ +import inspect +from typing import Optional + +from cx_const import ActionFunctionWithParams, PredefinedActionsMapping, TypeAction +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + + +def _get_action(action_value: TypeAction) -> ActionFunctionWithParams: + if isinstance(action_value, tuple): + return action_value + else: + return (action_value, tuple()) + + +class PredefinedActionType(ActionType): + action_key: str + predefined_actions_mapping: PredefinedActionsMapping + + def initialize(self, **kwargs) -> None: + self.action_key = kwargs["action"] + self.predefined_actions_mapping = ( + self.controller.get_predefined_actions_mapping() + ) + if self.action_key not in self.predefined_actions_mapping: + raise ValueError( + f"`{self.action_key}` is not one of the predefined actions." + f"Available actions are: {list(self.predefined_actions_mapping.keys())}" + ) + + async def run(self, extra: Optional[EventData] = None) -> None: + action, args = _get_action(self.predefined_actions_mapping[self.action_key]) + if "extra" in set(inspect.signature(action).parameters): + await action(*args, extra=extra) + else: + await action(*args) + + def __str__(self) -> str: + return f"Predefined ({self.action_key})" diff --git a/apps/controllerx/cx_core/action_type/scene_action_type.py b/apps/controllerx/cx_core/action_type/scene_action_type.py new file mode 100644 index 00000000..96b8d5ec --- /dev/null +++ b/apps/controllerx/cx_core/action_type/scene_action_type.py @@ -0,0 +1,17 @@ +from typing import Optional + +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + + +class SceneActionType(ActionType): + scene: str + + def initialize(self, **kwargs) -> None: + self.scene = kwargs["scene"] + + async def run(self, extra: Optional[EventData] = None) -> None: + await self.controller.call_service("scene/turn_on", entity_id=self.scene) + + def __str__(self) -> str: + return f"Scene ({self.scene})" diff --git a/apps/controllerx/cx_core/color_helper.py b/apps/controllerx/cx_core/color_helper.py index 685503c6..02cd359f 100644 --- a/apps/controllerx/cx_core/color_helper.py +++ b/apps/controllerx/cx_core/color_helper.py @@ -74,7 +74,7 @@ def get_color_wheel(colors: Union[str, Colors]) -> Colors: f"`{colors}` is not an option for `color_wheel`. Options are: {list(COLOR_WHEELS.keys())}" ) return COLOR_WHEELS[colors] - elif isinstance(colors, list): + elif isinstance(colors, (list, tuple)): return colors else: raise ValueError( diff --git a/apps/controllerx/cx_core/controller.py b/apps/controllerx/cx_core/controller.py index 17817dc8..7a178df0 100644 --- a/apps/controllerx/cx_core/controller.py +++ b/apps/controllerx/cx_core/controller.py @@ -1,5 +1,4 @@ import asyncio -import inspect import time from asyncio.futures import Future from collections import defaultdict @@ -12,7 +11,6 @@ Dict, List, Optional, - Sequence, Set, Tuple, TypeVar, @@ -25,11 +23,12 @@ from cx_const import ( ActionEvent, ActionFunction, - ActionsMapping, - TypeAction, - TypeActionsMapping, + CustomActionsMapping, + DefaultActionsMapping, + PredefinedActionsMapping, ) from cx_core import integration as integration_module +from cx_core.action_type import ActionsMapping, parse_actions from cx_core.integration import EventData, Integration Service = Tuple[str, Dict] @@ -76,11 +75,8 @@ class Controller(Hass, Mqtt): """ integration: Integration - controllers_ids: List[str] - actions_key_mapping: TypeActionsMapping actions_mapping: ActionsMapping multiple_click_actions: Set[ActionEvent] - type_actions_mapping: ActionsMapping action_delay: Dict[ActionEvent, int] action_delta: int action_times: Dict[str, float] @@ -95,70 +91,77 @@ async def initialize(self) -> None: self.check_ad_version() # Get arguments - self.controllers_ids = self.get_list(self.args["controller"]) + controllers_ids: List[str] = self.get_list(self.args["controller"]) self.integration = self.get_integration(self.args["integration"]) if "mapping" in self.args and "merge_mapping" in self.args: raise ValueError("`mapping` and `merge_mapping` cannot be used together") - custom_mapping: Dict[ActionEvent, str] = self.args.get("mapping", None) - merge_mapping: Dict[ActionEvent, str] = self.args.get("merge_mapping", None) + custom_mapping: CustomActionsMapping = self.args.get("mapping", None) + merge_mapping: CustomActionsMapping = self.args.get("merge_mapping", None) - self.actions_key_mapping = ( - self.get_actions_mapping(self.integration) - if custom_mapping is None - else self.parse_action_mapping(custom_mapping) - ) - if merge_mapping is not None: - self.actions_key_mapping.update(self.parse_action_mapping(merge_mapping)) + if custom_mapping is None: + default_actions_mapping = self.get_default_actions_mapping(self.integration) + self.actions_mapping = self.parse_action_mapping(default_actions_mapping) + else: + self.actions_mapping = self.parse_action_mapping(custom_mapping) - self.multiple_click_actions = self.get_multiple_click_actions( - self.actions_key_mapping - ) + if merge_mapping is not None: + self.actions_mapping.update(self.parse_action_mapping(merge_mapping)) - self.type_actions_mapping = self.get_type_actions_mapping() + # Filter actions with include and exclude if "actions" in self.args and "excluded_actions" in self.args: raise ValueError("`actions` and `excluded_actions` cannot be used together") - included_actions: Set[ActionEvent] = set( - self.get_list( - self.args.get("actions", list(self.actions_key_mapping.keys())) - ) + include: List[ActionEvent] = self.get_list( + self.args.get("actions", list(self.actions_mapping.keys())) ) - excluded_actions: Set[ActionEvent] = set( - self.get_list(self.args.get("excluded_actions", [])) + exclude: List[ActionEvent] = self.get_list( + self.args.get("excluded_actions", []) ) - included_actions = included_actions - excluded_actions - default_action_delay = {action_key: 0 for action_key in included_actions} + self.actions_mapping = self.filter_actions( + self.actions_mapping, set(include), set(exclude) + ) + + # Action delay + default_action_delay = {action_key: 0 for action_key in self.actions_mapping} self.action_delay = { **default_action_delay, **self.args.get("action_delay", {}), } + self.action_delay_handles = defaultdict(lambda: None) + + # Action delta self.action_delta = self.args.get("action_delta", DEFAULT_ACTION_DELTA) + self.action_times = defaultdict(lambda: 0.0) + + # Multiple click + self.multiple_click_actions = self.get_multiple_click_actions( + self.actions_mapping + ) self.multiple_click_delay = self.args.get( "multiple_click_delay", DEFAULT_MULTIPLE_CLICK_DELAY ) - self.action_times = defaultdict(lambda: 0.0) self.multiple_click_action_times = defaultdict(lambda: 0.0) self.click_counter = Counter() - self.action_delay_handles = defaultdict(lambda: None) self.multiple_click_action_delay_tasks = defaultdict(lambda: None) - # Filter the actions - filter_actions_mapping: TypeActionsMapping = { - key: value - for key, value in self.actions_key_mapping.items() - if key in included_actions - } + # Listen for device changes + for controller_id in controllers_ids: + self.integration.listen_changes(controller_id) - # Map the actions mapping with the real functions - self.actions_mapping = { - k: (self.type_actions_mapping[v] if isinstance(v, str) else v) - for k, v in filter_actions_mapping.items() + def filter_actions( + self, + actions_mapping: ActionsMapping, + include: Set[ActionEvent], + exclude: Set[ActionEvent], + ): + allowed_actions = include - exclude + return { + key: value + for key, value in actions_mapping.items() + if key in allowed_actions } - for controller_id in self.controllers_ids: - self.integration.listen_changes(controller_id) - def get_option(self, value: str, options: List[str]) -> str: if value in options: return value @@ -195,25 +198,23 @@ def check_ad_version(self) -> None: if int(major) < 4: raise ValueError("Please upgrade to AppDaemon 4.x") - def get_actions_mapping(self, integration: Integration) -> TypeActionsMapping: - actions_mapping = integration.get_actions_mapping() + def get_default_actions_mapping( + self, integration: Integration + ) -> DefaultActionsMapping: + actions_mapping = integration.get_default_actions_mapping() if actions_mapping is None: raise ValueError(f"This controller does not support {integration.name}.") return actions_mapping - def get_list(self, entities: Union[List[str], str]) -> List[str]: - if isinstance(entities, str): - return [entities] - return entities + def get_list(self, entities: Union[List[T], T]) -> List[T]: + if isinstance(entities, (list, tuple)): + return list(entities) + return [entities] - def parse_action_mapping( - self, mapping: Dict[ActionEvent, str] - ) -> TypeActionsMapping: - return {event: self.parse_action(action) for event, action in mapping.items()} + def parse_action_mapping(self, mapping: CustomActionsMapping) -> ActionsMapping: + return {event: parse_actions(self, action) for event, action in mapping.items()} - def get_multiple_click_actions( - self, mapping: TypeActionsMapping - ) -> Set[ActionEvent]: + def get_multiple_click_actions(self, mapping: ActionsMapping) -> Set[ActionEvent]: to_return: Set[ActionEvent] = set() for key in mapping.keys(): if not isinstance(key, str) or MULTIPLE_CLICK_TOKEN not in key: @@ -235,6 +236,7 @@ def format_multiple_click_action( ) # e.g. toggle$2 async def call_service(self, service: str, **attributes) -> None: + service = service.replace(".", "/") self.log( f"🤖 Service: \033[1m{service.replace('/', '.')}\033[0m", level="INFO", @@ -311,13 +313,17 @@ async def call_action( level="INFO", ascii_encode=False, ) + self.log( + f"Extra:\n{extra}", + level="DEBUG", + ) delay = self.action_delay[action_key] if delay > 0: handle = self.action_delay_handles[action_key] if handle is not None: await self.cancel_timer(handle) # type: ignore self.log( - f"🕒 Running `{self.actions_key_mapping[action_key]}` in {delay} seconds", + f"🕒 Running action(s) from `{action_key}` in {delay} seconds", level="INFO", ascii_encode=False, ) @@ -332,20 +338,14 @@ async def action_timer_callback(self, kwargs: Dict[str, Any]): action_key: ActionEvent = kwargs["action_key"] extra: EventData = kwargs["extra"] self.action_delay_handles[action_key] = None - self.log( - f"🏃 Running `{self.actions_key_mapping[action_key]}` now", - level="INFO", - ascii_encode=False, - ) - self.log( - f"Extra:\n{extra}", - level="DEBUG", - ) - action, *args = self.get_action(self.actions_mapping[action_key]) - if "extra" in set(inspect.signature(action).parameters): - await action(*args, extra=extra) - else: - await action(*args) + action_types = self.actions_mapping[action_key] + for action_type in action_types: + self.log( + f"🏃 Running `{action_type}` now", + level="INFO", + ascii_encode=False, + ) + await action_type.run(extra=extra) async def before_action( self, action: str, *args: str, **kwargs: Dict[Any, Any] @@ -358,39 +358,7 @@ async def before_action( """ return True - def get_action(self, action_value: Union[Sequence, Callable]): - if isinstance(action_value, tuple) or isinstance(action_value, list): - return action_value - elif callable(action_value): - return (action_value,) - else: - raise ValueError( - "The action value from the action mapping should be a list or a function" - ) - - def parse_action(self, action) -> TypeAction: - if isinstance(action, str): - return action - elif isinstance(action, dict) or isinstance(action, list): - services: Services = [] - if isinstance(action, dict): - action = [action] - for act in action: - service = act["service"].replace(".", "/") - data = act.get("data", {}) - services.append((service, data)) - return (self.call_services, services) - else: - raise ValueError( - f"{type(action)} is not supported for the mapping value attributes" - ) - - @action - async def call_services(self, services: Services) -> None: - for service, data in services: - await self.call_service(service, **data) - - def get_z2m_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_z2m_actions_mapping(self) -> Optional[DefaultActionsMapping]: """ Controllers can implement this function. It should return a dict with the states that a controller can take and the functions as values. @@ -398,7 +366,7 @@ def get_z2m_actions_mapping(self) -> Optional[TypeActionsMapping]: """ return None - def get_deconz_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_deconz_actions_mapping(self) -> Optional[DefaultActionsMapping]: """ Controllers can implement this function. It should return a dict with the event id that a controller can take and the functions as values. @@ -406,7 +374,7 @@ def get_deconz_actions_mapping(self) -> Optional[TypeActionsMapping]: """ return None - def get_zha_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_zha_actions_mapping(self) -> Optional[DefaultActionsMapping]: """ Controllers can implement this function. It should return a dict with the command that a controller can take and the functions as values. @@ -421,5 +389,5 @@ def get_zha_action(self, data: EventData) -> Optional[str]: """ return None - def get_type_actions_mapping(self) -> ActionsMapping: + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: return {} diff --git a/apps/controllerx/cx_core/integration/__init__.py b/apps/controllerx/cx_core/integration/__init__.py index 9a57cb5e..a638ba1e 100644 --- a/apps/controllerx/cx_core/integration/__init__.py +++ b/apps/controllerx/cx_core/integration/__init__.py @@ -4,7 +4,7 @@ import pkgutil from typing import TYPE_CHECKING, Any, Dict, List, NewType, Optional, Type, Union -from cx_const import TypeActionsMapping +from cx_const import DefaultActionsMapping if TYPE_CHECKING: from cx_core.controller import Controller @@ -23,7 +23,7 @@ def __init__(self, controller: "Controller", kwargs: Dict[str, Any]): self.kwargs = kwargs @abc.abstractmethod - def get_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_default_actions_mapping(self) -> Optional[DefaultActionsMapping]: raise NotImplementedError @abc.abstractmethod diff --git a/apps/controllerx/cx_core/integration/deconz.py b/apps/controllerx/cx_core/integration/deconz.py index af331885..25895f5c 100644 --- a/apps/controllerx/cx_core/integration/deconz.py +++ b/apps/controllerx/cx_core/integration/deconz.py @@ -1,13 +1,14 @@ from typing import Optional -from appdaemon.plugins.hass.hassapi import Hass # type:ignore -from cx_core.integration import EventData, Integration, TypeActionsMapping +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping # type:ignore +from cx_core.integration import EventData, Integration class DeCONZIntegration(Integration): name = "deconz" - def get_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_default_actions_mapping(self) -> Optional[DefaultActionsMapping]: return self.controller.get_deconz_actions_mapping() def listen_changes(self, controller_id: str) -> None: diff --git a/apps/controllerx/cx_core/integration/mqtt.py b/apps/controllerx/cx_core/integration/mqtt.py index cf9d67ca..84c040bf 100644 --- a/apps/controllerx/cx_core/integration/mqtt.py +++ b/apps/controllerx/cx_core/integration/mqtt.py @@ -1,14 +1,14 @@ from typing import Optional from appdaemon.plugins.mqtt.mqttapi import Mqtt # type: ignore -from cx_const import TypeActionsMapping +from cx_const import DefaultActionsMapping from cx_core.integration import EventData, Integration class MQTTIntegration(Integration): name = "mqtt" - def get_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_default_actions_mapping(self) -> Optional[DefaultActionsMapping]: return self.controller.get_z2m_actions_mapping() def listen_changes(self, controller_id: str) -> None: diff --git a/apps/controllerx/cx_core/integration/state.py b/apps/controllerx/cx_core/integration/state.py index 520aa125..96dd6ba4 100644 --- a/apps/controllerx/cx_core/integration/state.py +++ b/apps/controllerx/cx_core/integration/state.py @@ -1,14 +1,14 @@ from typing import Optional from appdaemon.plugins.hass.hassapi import Hass # type: ignore -from cx_const import TypeActionsMapping +from cx_const import DefaultActionsMapping from cx_core.integration import Integration class StateIntegration(Integration): name = "state" - def get_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_default_actions_mapping(self) -> Optional[DefaultActionsMapping]: return self.controller.get_z2m_actions_mapping() def listen_changes(self, controller_id: str) -> None: diff --git a/apps/controllerx/cx_core/integration/z2m.py b/apps/controllerx/cx_core/integration/z2m.py index 95c161bb..295f89bc 100644 --- a/apps/controllerx/cx_core/integration/z2m.py +++ b/apps/controllerx/cx_core/integration/z2m.py @@ -3,7 +3,7 @@ from appdaemon.plugins.hass.hassapi import Hass # type: ignore from appdaemon.plugins.mqtt.mqttapi import Mqtt # type: ignore -from cx_const import TypeActionsMapping +from cx_const import DefaultActionsMapping from cx_core.integration import EventData, Integration LISTENS_TO_HA = "ha" @@ -13,7 +13,7 @@ class Z2MIntegration(Integration): name = "z2m" - def get_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_default_actions_mapping(self) -> Optional[DefaultActionsMapping]: return self.controller.get_z2m_actions_mapping() def listen_changes(self, controller_id: str) -> None: diff --git a/apps/controllerx/cx_core/integration/zha.py b/apps/controllerx/cx_core/integration/zha.py index 02db43de..0c4b9e42 100644 --- a/apps/controllerx/cx_core/integration/zha.py +++ b/apps/controllerx/cx_core/integration/zha.py @@ -1,14 +1,14 @@ from typing import Optional from appdaemon.plugins.hass.hassapi import Hass # type: ignore -from cx_const import TypeActionsMapping +from cx_const import DefaultActionsMapping from cx_core.integration import EventData, Integration class ZHAIntegration(Integration): name = "zha" - def get_actions_mapping(self) -> Optional[TypeActionsMapping]: + def get_default_actions_mapping(self) -> Optional[DefaultActionsMapping]: return self.controller.get_zha_actions_mapping() def listen_changes(self, controller_id: str) -> None: diff --git a/apps/controllerx/cx_core/type/cover_controller.py b/apps/controllerx/cx_core/type/cover_controller.py index 37a51d40..c7c96ef2 100644 --- a/apps/controllerx/cx_core/type/cover_controller.py +++ b/apps/controllerx/cx_core/type/cover_controller.py @@ -1,6 +1,6 @@ from typing import Callable, Type -from cx_const import ActionsMapping, Cover +from cx_const import Cover, PredefinedActionsMapping from cx_core.controller import action from cx_core.feature_support.cover import CoverSupport from cx_core.type_controller import Entity, TypeController @@ -35,13 +35,13 @@ async def initialize(self) -> None: def _get_entity_type(self) -> Type[Entity]: return Entity - def get_type_actions_mapping(self) -> ActionsMapping: + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: return { Cover.OPEN: self.open, Cover.CLOSE: self.close, Cover.STOP: self.stop, - Cover.TOGGLE_OPEN: (self.toggle, self.open), - Cover.TOGGLE_CLOSE: (self.toggle, self.close), + Cover.TOGGLE_OPEN: (self.toggle, (self.open,)), + Cover.TOGGLE_CLOSE: (self.toggle, (self.close,)), } @action diff --git a/apps/controllerx/cx_core/type/light_controller.py b/apps/controllerx/cx_core/type/light_controller.py index f96fefd5..a6c30e3b 100644 --- a/apps/controllerx/cx_core/type/light_controller.py +++ b/apps/controllerx/cx_core/type/light_controller.py @@ -1,6 +1,6 @@ from typing import Any, Dict, Optional, Type, Union -from cx_const import ActionsMapping, Light +from cx_const import Light, PredefinedActionsMapping from cx_core.color_helper import get_color_wheel from cx_core.controller import action from cx_core.feature_support.light import LightSupport @@ -120,200 +120,256 @@ async def initialize(self) -> None: def _get_entity_type(self) -> Type[LightEntity]: return LightEntity - def get_type_actions_mapping(self) -> ActionsMapping: + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: return { Light.ON: self.on, Light.OFF: self.off, Light.TOGGLE: self.toggle, Light.TOGGLE_FULL_BRIGHTNESS: ( self.toggle_full, - LightController.ATTRIBUTE_BRIGHTNESS, + (LightController.ATTRIBUTE_BRIGHTNESS,), ), Light.TOGGLE_FULL_WHITE_VALUE: ( self.toggle_full, - LightController.ATTRIBUTE_WHITE_VALUE, + (LightController.ATTRIBUTE_WHITE_VALUE,), ), Light.TOGGLE_FULL_COLOR_TEMP: ( self.toggle_full, - LightController.ATTRIBUTE_COLOR_TEMP, + (LightController.ATTRIBUTE_COLOR_TEMP,), ), Light.TOGGLE_MIN_BRIGHTNESS: ( self.toggle_min, - LightController.ATTRIBUTE_BRIGHTNESS, + (LightController.ATTRIBUTE_BRIGHTNESS,), ), Light.TOGGLE_MIN_WHITE_VALUE: ( self.toggle_min, - LightController.ATTRIBUTE_WHITE_VALUE, + (LightController.ATTRIBUTE_WHITE_VALUE,), ), Light.TOGGLE_MIN_COLOR_TEMP: ( self.toggle_min, - LightController.ATTRIBUTE_COLOR_TEMP, + (LightController.ATTRIBUTE_COLOR_TEMP,), ), Light.RELEASE: self.release, Light.ON_FULL_BRIGHTNESS: ( self.on_full, - LightController.ATTRIBUTE_BRIGHTNESS, + (LightController.ATTRIBUTE_BRIGHTNESS,), ), Light.ON_FULL_WHITE_VALUE: ( self.on_full, - LightController.ATTRIBUTE_WHITE_VALUE, + (LightController.ATTRIBUTE_WHITE_VALUE,), ), Light.ON_FULL_COLOR_TEMP: ( self.on_full, - LightController.ATTRIBUTE_COLOR_TEMP, + (LightController.ATTRIBUTE_COLOR_TEMP,), ), Light.ON_MIN_BRIGHTNESS: ( self.on_min, - LightController.ATTRIBUTE_BRIGHTNESS, + (LightController.ATTRIBUTE_BRIGHTNESS,), ), Light.ON_MIN_WHITE_VALUE: ( self.on_min, - LightController.ATTRIBUTE_WHITE_VALUE, + (LightController.ATTRIBUTE_WHITE_VALUE,), ), Light.ON_MIN_COLOR_TEMP: ( self.on_min, - LightController.ATTRIBUTE_COLOR_TEMP, + (LightController.ATTRIBUTE_COLOR_TEMP,), ), Light.SET_HALF_BRIGHTNESS: ( self.set_value, - LightController.ATTRIBUTE_BRIGHTNESS, - 0.5, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + 0.5, + ), ), Light.SET_HALF_WHITE_VALUE: ( self.set_value, - LightController.ATTRIBUTE_WHITE_VALUE, - 0.5, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + 0.5, + ), ), Light.SET_HALF_COLOR_TEMP: ( self.set_value, - LightController.ATTRIBUTE_COLOR_TEMP, - 0.5, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + 0.5, + ), ), Light.SYNC: self.sync, Light.CLICK_BRIGHTNESS_UP: ( self.click, - LightController.ATTRIBUTE_BRIGHTNESS, - Stepper.UP, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + Stepper.UP, + ), ), Light.CLICK_BRIGHTNESS_DOWN: ( self.click, - LightController.ATTRIBUTE_BRIGHTNESS, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + Stepper.DOWN, + ), ), Light.CLICK_WHITE_VALUE_UP: ( self.click, - LightController.ATTRIBUTE_WHITE_VALUE, - Stepper.UP, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + Stepper.UP, + ), ), Light.CLICK_WHITE_VALUE_DOWN: ( self.click, - LightController.ATTRIBUTE_WHITE_VALUE, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + Stepper.DOWN, + ), ), Light.CLICK_COLOR_UP: ( self.click, - LightController.ATTRIBUTE_COLOR, - Stepper.UP, + ( + LightController.ATTRIBUTE_COLOR, + Stepper.UP, + ), ), Light.CLICK_COLOR_DOWN: ( self.click, - LightController.ATTRIBUTE_COLOR, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_COLOR, + Stepper.DOWN, + ), ), Light.CLICK_COLOR_TEMP_UP: ( self.click, - LightController.ATTRIBUTE_COLOR_TEMP, - Stepper.UP, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + Stepper.UP, + ), ), Light.CLICK_COLOR_TEMP_DOWN: ( self.click, - LightController.ATTRIBUTE_COLOR_TEMP, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + Stepper.DOWN, + ), ), Light.CLICK_XY_COLOR_UP: ( self.click, - LightController.ATTRIBUTE_XY_COLOR, - Stepper.UP, + ( + LightController.ATTRIBUTE_XY_COLOR, + Stepper.UP, + ), ), Light.CLICK_XY_COLOR_DOWN: ( self.click, - LightController.ATTRIBUTE_XY_COLOR, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_XY_COLOR, + Stepper.DOWN, + ), ), Light.HOLD_BRIGHTNESS_UP: ( self.hold, - LightController.ATTRIBUTE_BRIGHTNESS, - Stepper.UP, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + Stepper.UP, + ), ), Light.HOLD_BRIGHTNESS_DOWN: ( self.hold, - LightController.ATTRIBUTE_BRIGHTNESS, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + Stepper.DOWN, + ), ), Light.HOLD_BRIGHTNESS_TOGGLE: ( self.hold, - LightController.ATTRIBUTE_BRIGHTNESS, - Stepper.TOGGLE, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + Stepper.TOGGLE, + ), ), Light.HOLD_WHITE_VALUE_UP: ( self.hold, - LightController.ATTRIBUTE_WHITE_VALUE, - Stepper.UP, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + Stepper.UP, + ), ), Light.HOLD_WHITE_VALUE_DOWN: ( self.hold, - LightController.ATTRIBUTE_WHITE_VALUE, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + Stepper.DOWN, + ), ), Light.HOLD_WHITE_VALUE_TOGGLE: ( self.hold, - LightController.ATTRIBUTE_WHITE_VALUE, - Stepper.TOGGLE, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + Stepper.TOGGLE, + ), ), Light.HOLD_COLOR_UP: ( self.hold, - LightController.ATTRIBUTE_COLOR, - Stepper.UP, + ( + LightController.ATTRIBUTE_COLOR, + Stepper.UP, + ), ), Light.HOLD_COLOR_DOWN: ( self.hold, - LightController.ATTRIBUTE_COLOR, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_COLOR, + Stepper.DOWN, + ), ), Light.HOLD_COLOR_TOGGLE: ( self.hold, - LightController.ATTRIBUTE_COLOR, - Stepper.TOGGLE, + ( + LightController.ATTRIBUTE_COLOR, + Stepper.TOGGLE, + ), ), Light.HOLD_COLOR_TEMP_UP: ( self.hold, - LightController.ATTRIBUTE_COLOR_TEMP, - Stepper.UP, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + Stepper.UP, + ), ), Light.HOLD_COLOR_TEMP_DOWN: ( self.hold, - LightController.ATTRIBUTE_COLOR_TEMP, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + Stepper.DOWN, + ), ), Light.HOLD_COLOR_TEMP_TOGGLE: ( self.hold, - LightController.ATTRIBUTE_COLOR_TEMP, - Stepper.TOGGLE, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + Stepper.TOGGLE, + ), ), Light.HOLD_XY_COLOR_UP: ( self.hold, - LightController.ATTRIBUTE_XY_COLOR, - Stepper.UP, + ( + LightController.ATTRIBUTE_XY_COLOR, + Stepper.UP, + ), ), Light.HOLD_XY_COLOR_DOWN: ( self.hold, - LightController.ATTRIBUTE_XY_COLOR, - Stepper.DOWN, + ( + LightController.ATTRIBUTE_XY_COLOR, + Stepper.DOWN, + ), ), Light.HOLD_XY_COLOR_TOGGLE: ( self.hold, - LightController.ATTRIBUTE_XY_COLOR, - Stepper.TOGGLE, + ( + LightController.ATTRIBUTE_XY_COLOR, + Stepper.TOGGLE, + ), ), } diff --git a/apps/controllerx/cx_core/type/media_player_controller.py b/apps/controllerx/cx_core/type/media_player_controller.py index dccfbf8a..06e565b2 100644 --- a/apps/controllerx/cx_core/type/media_player_controller.py +++ b/apps/controllerx/cx_core/type/media_player_controller.py @@ -1,6 +1,6 @@ from typing import Type -from cx_const import ActionsMapping, MediaPlayer +from cx_const import MediaPlayer, PredefinedActionsMapping from cx_core.controller import action from cx_core.feature_support.media_player import MediaPlayerSupport from cx_core.release_hold_controller import ReleaseHoldController @@ -26,10 +26,10 @@ async def initialize(self) -> None: def _get_entity_type(self) -> Type[Entity]: return Entity - def get_type_actions_mapping(self) -> ActionsMapping: + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: return { - MediaPlayer.HOLD_VOLUME_DOWN: (self.hold, Stepper.DOWN), - MediaPlayer.HOLD_VOLUME_UP: (self.hold, Stepper.UP), + MediaPlayer.HOLD_VOLUME_DOWN: (self.hold, (Stepper.DOWN,)), + MediaPlayer.HOLD_VOLUME_UP: (self.hold, (Stepper.UP,)), MediaPlayer.CLICK_VOLUME_DOWN: self.volume_down, MediaPlayer.CLICK_VOLUME_UP: self.volume_up, MediaPlayer.RELEASE: self.release, @@ -38,8 +38,8 @@ def get_type_actions_mapping(self) -> ActionsMapping: MediaPlayer.PLAY_PAUSE: self.play_pause, MediaPlayer.NEXT_TRACK: self.next_track, MediaPlayer.PREVIOUS_TRACK: self.previous_track, - MediaPlayer.NEXT_SOURCE: (self.change_source_list, Stepper.UP), - MediaPlayer.PREVIOUS_SOURCE: (self.change_source_list, Stepper.DOWN), + MediaPlayer.NEXT_SOURCE: (self.change_source_list, (Stepper.UP,)), + MediaPlayer.PREVIOUS_SOURCE: (self.change_source_list, (Stepper.DOWN,)), } @action diff --git a/apps/controllerx/cx_core/type/switch_controller.py b/apps/controllerx/cx_core/type/switch_controller.py index f8a9e5d3..26b6bc64 100644 --- a/apps/controllerx/cx_core/type/switch_controller.py +++ b/apps/controllerx/cx_core/type/switch_controller.py @@ -1,6 +1,6 @@ from typing import Type -from cx_const import ActionsMapping, Switch +from cx_const import PredefinedActionsMapping, Switch from cx_core.controller import action from cx_core.type_controller import Entity, TypeController @@ -27,7 +27,7 @@ class SwitchController(TypeController[Entity]): ] entity_arg = "switch" - def get_type_actions_mapping(self) -> ActionsMapping: + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: return { Switch.ON: self.on, Switch.OFF: self.off, diff --git a/apps/controllerx/cx_devices/aqara.py b/apps/controllerx/cx_devices/aqara.py index c1c9acd6..c3a4d903 100644 --- a/apps/controllerx/cx_devices/aqara.py +++ b/apps/controllerx/cx_devices/aqara.py @@ -1,10 +1,10 @@ -from cx_const import Light, Switch, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light, Switch from cx_core import LightController, SwitchController from cx_core.integration import EventData class WXKG02LMLightController(LightController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "single_both": Light.TOGGLE, "double_both": Light.CLICK_BRIGHTNESS_UP, @@ -17,7 +17,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "hold_right": Light.CLICK_BRIGHTNESS_DOWN, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, # single left 1001: Light.CLICK_BRIGHTNESS_DOWN, # long left @@ -32,14 +32,14 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: class WXKG02LMSwitchController(SwitchController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "single_both": Switch.TOGGLE, "single_left": Switch.TOGGLE, "single_right": Switch.TOGGLE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Switch.TOGGLE, 2002: Switch.TOGGLE, @@ -54,7 +54,7 @@ class WXKG01LMLightController(LightController): many, hold, release """ - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "single": Light.TOGGLE, "double": Light.ON_FULL_BRIGHTNESS, @@ -65,7 +65,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "release": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, # single 1004: Light.ON_FULL_BRIGHTNESS, # double @@ -76,7 +76,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 1000: Light.RELEASE, # release the button } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "single": Light.TOGGLE, "double": Light.ON_FULL_BRIGHTNESS, @@ -89,7 +89,7 @@ def get_zha_action(self, data: EventData) -> str: class WXKG11LMRemoteLightController(LightController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "single": Light.TOGGLE, "double": Light.ON_FULL_BRIGHTNESS, @@ -97,7 +97,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "release": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, 1004: Light.ON_FULL_BRIGHTNESS, @@ -105,7 +105,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 1003: Light.RELEASE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "single": Light.TOGGLE, "double": Light.ON_FULL_BRIGHTNESS, @@ -118,7 +118,7 @@ def get_zha_action(self, data: EventData) -> str: class WXKG11LMSensorSwitchLightController(LightController): - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "single": Light.TOGGLE, "double": Light.ON_FULL_BRIGHTNESS, @@ -143,7 +143,7 @@ class WXKG12LMLightController(LightController): single, double, shake, hold, release """ - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "single": Light.TOGGLE, "double": Light.ON_FULL_BRIGHTNESS, @@ -152,7 +152,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "release": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, # button_1_press 1004: Light.ON_FULL_BRIGHTNESS, # button_1_double_press @@ -174,7 +174,7 @@ class MFKZQ01LMLightController(LightController): # shake, wakeup, fall, tap, slide, flip180 # flip90, rotate_left and rotate_right - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "shake": Light.ON_MIN_BRIGHTNESS, "tap": Light.TOGGLE, @@ -185,7 +185,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "rotate_right": Light.CLICK_BRIGHTNESS_UP, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1: Light.ON_MIN_BRIGHTNESS, 6: Light.TOGGLE, @@ -196,7 +196,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 7: Light.CLICK_BRIGHTNESS_UP, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "shake": Light.ON_MIN_BRIGHTNESS, "knock": Light.TOGGLE, @@ -216,7 +216,7 @@ def get_zha_action(self, data: EventData) -> str: class WXCJKG11LMLightController(LightController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "button_1_single": Light.OFF, "button_1_double": Light.ON_MIN_BRIGHTNESS, @@ -230,7 +230,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: class WXCJKG12LMLightController(LightController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "button_1_single": Light.OFF, "button_1_double": Light.ON_MIN_COLOR_TEMP, @@ -254,7 +254,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "button_4_release": Light.RELEASE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "1_single": Light.OFF, "1_double": Light.ON_MIN_COLOR_TEMP, @@ -283,7 +283,7 @@ def get_zha_action(self, data: EventData) -> str: class WXCJKG13LMLightController(LightController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "button_1_single": Light.OFF, "button_1_double": Light.SYNC, @@ -317,7 +317,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "button_6_release": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.OFF, 1004: Light.SYNC, diff --git a/apps/controllerx/cx_devices/ikea.py b/apps/controllerx/cx_devices/ikea.py index e3186379..197b676c 100644 --- a/apps/controllerx/cx_devices/ikea.py +++ b/apps/controllerx/cx_devices/ikea.py @@ -1,10 +1,10 @@ from cx_const import ( - ActionsMapping, Cover, + DefaultActionsMapping, Light, MediaPlayer, + PredefinedActionsMapping, Switch, - TypeActionsMapping, ) from cx_core import ( CoverController, @@ -23,7 +23,7 @@ class E1810Controller(LightController): # arrow_left_hold, arrow_left_release, arrow_right_hold # arrow_right_release - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "toggle": Light.TOGGLE, "toggle_hold": Light.SYNC, @@ -41,7 +41,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "arrow_right_release": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, 1001: Light.SYNC, @@ -59,7 +59,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 5003: Light.RELEASE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "toggle": Light.TOGGLE, "press_2_0_0": Light.SYNC, @@ -86,7 +86,7 @@ class E1810MediaPlayerController(MediaPlayerController): # arrow_left_hold, arrow_left_release, arrow_right_hold # arrow_right_release - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "toggle": MediaPlayer.PLAY_PAUSE, "brightness_up_click": MediaPlayer.CLICK_VOLUME_UP, @@ -101,7 +101,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "brightness_down_release": MediaPlayer.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: MediaPlayer.PLAY_PAUSE, 2002: MediaPlayer.CLICK_VOLUME_UP, @@ -114,7 +114,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 3003: MediaPlayer.RELEASE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "toggle": MediaPlayer.PLAY_PAUSE, "step_with_on_off_0_43_5": MediaPlayer.CLICK_VOLUME_UP, @@ -134,7 +134,7 @@ class E1743Controller(LightController): # Different states reported from the controller: # on, off, brightness_up, brightness_down, brightness_stop - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on": Light.ON, "off": Light.OFF, @@ -143,7 +143,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "brightness_stop": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.ON, 2002: Light.OFF, @@ -153,7 +153,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 2003: Light.RELEASE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "on": Light.ON, "off": Light.OFF, @@ -167,7 +167,7 @@ class E1743MediaPlayerController(MediaPlayerController): # Different states reported from the controller: # on, off, brightness_up, brightness_down, brightness_stop - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on": MediaPlayer.PLAY_PAUSE, "off": MediaPlayer.NEXT_TRACK, @@ -176,7 +176,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "brightness_stop": MediaPlayer.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: MediaPlayer.PLAY_PAUSE, 2002: MediaPlayer.NEXT_TRACK, @@ -186,7 +186,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 2003: MediaPlayer.RELEASE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "on": MediaPlayer.PLAY_PAUSE, "off": MediaPlayer.NEXT_TRACK, @@ -200,13 +200,13 @@ class E1743SwitchController(SwitchController): # Different states reported from the controller: # on, off - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return {"on": Switch.ON, "off": Switch.OFF} - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return {1002: Switch.ON, 2002: Switch.OFF} - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return {"on": Switch.ON, "off": Switch.OFF} @@ -214,7 +214,7 @@ class E1743CoverController(CoverController): # Different states reported from the controller: # on, off - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on": Cover.TOGGLE_OPEN, "off": Cover.TOGGLE_CLOSE, @@ -223,7 +223,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "brightness_stop": Cover.STOP, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Cover.TOGGLE_OPEN, 2002: Cover.TOGGLE_CLOSE, @@ -233,7 +233,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 2003: Cover.STOP, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "on": Cover.TOGGLE_OPEN, "off": Cover.TOGGLE_CLOSE, @@ -259,16 +259,16 @@ async def rotate_right_quick(self) -> None: await self.release() await self.on_full(LightController.ATTRIBUTE_BRIGHTNESS) - def get_type_actions_mapping(self) -> ActionsMapping: - parent_mapping = super().get_type_actions_mapping() - mapping: ActionsMapping = { + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + parent_mapping = super().get_predefined_actions_mapping() + mapping: PredefinedActionsMapping = { "rotate_left_quick": self.rotate_left_quick, "rotate_right_quick": self.rotate_right_quick, } mapping.update(parent_mapping) return mapping - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "rotate_left": Light.HOLD_BRIGHTNESS_DOWN, "rotate_left_quick": "rotate_left_quick", @@ -277,7 +277,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "rotate_stop": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: "rotate_right_quick", 2002: Light.CLICK_BRIGHTNESS_UP, @@ -285,7 +285,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 4002: "rotate_left_quick", } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "move_1_70": Light.HOLD_BRIGHTNESS_DOWN, "move_1_195": Light.HOLD_BRIGHTNESS_DOWN, @@ -313,16 +313,16 @@ async def rotate_right_quick(self) -> None: await self.release() await self.play() - def get_type_actions_mapping(self) -> ActionsMapping: - parent_mapping = super().get_type_actions_mapping() - mapping: ActionsMapping = { + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + parent_mapping = super().get_predefined_actions_mapping() + mapping: PredefinedActionsMapping = { "rotate_left_quick": self.rotate_left_quick, "rotate_right_quick": self.rotate_right_quick, } mapping.update(parent_mapping) return mapping - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "rotate_left": MediaPlayer.HOLD_VOLUME_DOWN, "rotate_left_quick": "rotate_left_quick", @@ -331,7 +331,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "rotate_stop": MediaPlayer.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: "rotate_right_quick", 2002: MediaPlayer.CLICK_VOLUME_UP, @@ -339,7 +339,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 4002: "rotate_left_quick", } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "move_1_70": MediaPlayer.HOLD_VOLUME_DOWN, "move_1_195": MediaPlayer.HOLD_VOLUME_DOWN, @@ -356,7 +356,7 @@ class E1744LightController(LightController): # brightness_move_down, brightness_move_up, brightness_stop, # toggle, brightness_step_up, brightness_step_down - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, @@ -366,7 +366,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "brightness_step_down": Light.ON_MIN_BRIGHTNESS, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 3001: Light.HOLD_BRIGHTNESS_DOWN, 2001: Light.HOLD_BRIGHTNESS_UP, @@ -377,7 +377,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 1005: Light.ON_MIN_BRIGHTNESS, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "move_1_195": Light.HOLD_BRIGHTNESS_DOWN, "move_0_195": Light.HOLD_BRIGHTNESS_UP, @@ -396,7 +396,7 @@ class E1744MediaPlayerController(MediaPlayerController): # brightness_move_down, brightness_move_up, brightness_stop, # toggle, brightness_step_up, brightness_step_down - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "brightness_move_down": MediaPlayer.HOLD_VOLUME_DOWN, "brightness_move_up": MediaPlayer.HOLD_VOLUME_UP, @@ -406,7 +406,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "brightness_step_down": MediaPlayer.PREVIOUS_TRACK, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 2001: MediaPlayer.HOLD_VOLUME_UP, 3001: MediaPlayer.HOLD_VOLUME_DOWN, @@ -417,7 +417,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 1005: MediaPlayer.PREVIOUS_TRACK, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "move_1_195": MediaPlayer.HOLD_VOLUME_DOWN, "move_0_195": MediaPlayer.HOLD_VOLUME_UP, @@ -432,10 +432,10 @@ def default_delay(self) -> int: class E1766LightController(LightController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return {"open": Light.ON, "close": Light.OFF} - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.ON, 1003: Light.ON_FULL_BRIGHTNESS, @@ -443,7 +443,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 2003: Light.ON_MIN_BRIGHTNESS, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "up_open": Light.ON, "down_close": Light.OFF, @@ -451,27 +451,27 @@ def get_zha_actions_mapping(self) -> TypeActionsMapping: class E1766SwitchController(SwitchController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return {"open": Switch.ON, "close": Switch.OFF} - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return {1002: Switch.ON, 2002: Switch.OFF} - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return {"up_open": Switch.ON, "down_close": Switch.OFF} class E1766CoverController(CoverController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return {"open": Cover.TOGGLE_OPEN, "close": Cover.TOGGLE_CLOSE} - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Cover.TOGGLE_OPEN, 2002: Cover.TOGGLE_CLOSE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "up_open": Cover.TOGGLE_OPEN, "down_close": Cover.TOGGLE_CLOSE, diff --git a/apps/controllerx/cx_devices/legrand.py b/apps/controllerx/cx_devices/legrand.py index 4676e5f4..3e7f2e8c 100644 --- a/apps/controllerx/cx_devices/legrand.py +++ b/apps/controllerx/cx_devices/legrand.py @@ -1,4 +1,4 @@ -from cx_const import Light, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light from cx_core import LightController from cx_core.integration import EventData @@ -15,7 +15,7 @@ def get_zha_action_LegrandWallController(data: dict) -> str: class Legrand600083LightController(LightController): - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "1_on": Light.ON, "1_off": Light.OFF, @@ -29,7 +29,7 @@ def get_zha_action(self, data: EventData) -> str: class Legrand600088LightController(LightController): - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "1_on": Light.ON, "1_off": Light.OFF, diff --git a/apps/controllerx/cx_devices/livarno.py b/apps/controllerx/cx_devices/livarno.py index c1a0e2a7..77373aeb 100644 --- a/apps/controllerx/cx_devices/livarno.py +++ b/apps/controllerx/cx_devices/livarno.py @@ -1,4 +1,4 @@ -from cx_const import Light, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light from cx_core import LightController @@ -7,7 +7,7 @@ class HG06323LightController(LightController): # on, off, brightness_step_up, brightness_move_up, # brightness_step_down, brightness_move_down, brightness_stop - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on": Light.ON, "brightness_step_up": Light.CLICK_BRIGHTNESS_UP, diff --git a/apps/controllerx/cx_devices/lutron.py b/apps/controllerx/cx_devices/lutron.py index 37bd77a4..94b2e867 100644 --- a/apps/controllerx/cx_devices/lutron.py +++ b/apps/controllerx/cx_devices/lutron.py @@ -1,4 +1,4 @@ -from cx_const import Light, MediaPlayer, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light, MediaPlayer from cx_core import LightController, MediaPlayerController @@ -10,7 +10,7 @@ class LutronCasetaProPicoLightController(LightController): # top button = "1", up button = "8", middle round = "2", down arrow = "16", # bottom button = "4", no button pressed = "0" - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "1": Light.ON_FULL_BRIGHTNESS, "8": Light.HOLD_BRIGHTNESS_UP, @@ -29,7 +29,7 @@ class LutronCasetaProPicoMediaPlayerController(MediaPlayerController): # top button = "1", up button = "8", middle round = "2", down arrow = "16", # bottom button = "4", no button pressed = "0" - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "1": MediaPlayer.PLAY_PAUSE, "8": MediaPlayer.HOLD_VOLUME_UP, @@ -48,7 +48,7 @@ class LutronCasetaProPJ24BLightController(LightController): # top button = "1", second button = "2", third button = "4", # bottom button = "8", no button pressed = "0" - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "1": Light.ON_FULL_BRIGHTNESS, "2": Light.HOLD_BRIGHTNESS_UP, @@ -66,7 +66,7 @@ class LutronCasetaProPJ24BMediaPlayerController(MediaPlayerController): # top button = "1", second button = "2", third button = "4", # bottom button = "8", no button pressed = "0" - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "1": MediaPlayer.PLAY_PAUSE, "2": MediaPlayer.HOLD_VOLUME_UP, @@ -81,7 +81,7 @@ class LZL4BWHL01LightController(LightController): # hold event. Press of up or down generates a stop event # when released. - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.ON_FULL_BRIGHTNESS, 2001: Light.HOLD_BRIGHTNESS_UP, @@ -91,7 +91,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 4002: Light.OFF, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "move_to_level_with_on_off_254_4": Light.ON_FULL_BRIGHTNESS, "step_with_on_off_0_30_6": Light.HOLD_BRIGHTNESS_UP, @@ -102,7 +102,7 @@ def get_zha_actions_mapping(self) -> TypeActionsMapping: class Z31BRLLightController(LightController): - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, 2002: Light.CLICK_BRIGHTNESS_UP, diff --git a/apps/controllerx/cx_devices/muller_licht.py b/apps/controllerx/cx_devices/muller_licht.py index 8d8da061..e6cc28c9 100644 --- a/apps/controllerx/cx_devices/muller_licht.py +++ b/apps/controllerx/cx_devices/muller_licht.py @@ -1,4 +1,4 @@ -from cx_const import ActionsMapping, Light, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light, PredefinedActionsMapping from cx_core import LightController from cx_core.controller import action from cx_core.integration import EventData @@ -14,15 +14,15 @@ async def change_xy_color(self, extra: EventData) -> None: if isinstance(self.integration, DeCONZIntegration): await self.on(xy_color=extra["xy"]) - def get_type_actions_mapping(self) -> ActionsMapping: - parent_mapping = super().get_type_actions_mapping() - mapping: ActionsMapping = { + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + parent_mapping = super().get_predefined_actions_mapping() + mapping: PredefinedActionsMapping = { MLI404011LightController.CHANGE_XY_COLOR: self.change_xy_color, } parent_mapping.update(mapping) return parent_mapping - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on": Light.TOGGLE, "off": Light.TOGGLE, @@ -42,7 +42,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: # "scene_5": "", # heart button } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, 2001: Light.HOLD_BRIGHTNESS_UP, diff --git a/apps/controllerx/cx_devices/osram.py b/apps/controllerx/cx_devices/osram.py index 9792d7d6..9b987ceb 100644 --- a/apps/controllerx/cx_devices/osram.py +++ b/apps/controllerx/cx_devices/osram.py @@ -1,4 +1,4 @@ -from cx_const import Light, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light from cx_core import LightController from cx_core.integration import EventData @@ -6,7 +6,7 @@ class OsramAC025XX00NJLightController(LightController): # This mapping works for: AC0251100NJ / AC0251400NJ / AC0251600NJ / AC0251700NJ # (different models are just different colours) - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "1_on": Light.ON, "1_move_with_on_off": Light.HOLD_BRIGHTNESS_UP, diff --git a/apps/controllerx/cx_devices/phillips.py b/apps/controllerx/cx_devices/phillips.py index 025badd7..9448a042 100644 --- a/apps/controllerx/cx_devices/phillips.py +++ b/apps/controllerx/cx_devices/phillips.py @@ -1,4 +1,4 @@ -from cx_const import Light, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light from cx_core import LightController from cx_core.integration import EventData @@ -9,7 +9,7 @@ class HueDimmerController(LightController): # up-hold-release, down-press, down-hold, down-hold-release, # off-press, off-hold, off-hold-release - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on-press": Light.ON, "on-hold": Light.HOLD_COLOR_UP, @@ -25,7 +25,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: "off-hold-release": Light.RELEASE, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.ON, 1001: Light.HOLD_COLOR_UP, @@ -41,7 +41,7 @@ def get_deconz_actions_mapping(self) -> TypeActionsMapping: 4003: Light.RELEASE, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "off_long_release": Light.RELEASE, "off_hold": Light.HOLD_COLOR_DOWN, @@ -62,7 +62,7 @@ def get_zha_action(self, data: EventData) -> str: class Niko91004LightController(LightController): - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.ON, # button_1_release 1001: Light.SYNC, # button_1_hold diff --git a/apps/controllerx/cx_devices/smartthings.py b/apps/controllerx/cx_devices/smartthings.py index 5d2b0d54..ebccf7de 100644 --- a/apps/controllerx/cx_devices/smartthings.py +++ b/apps/controllerx/cx_devices/smartthings.py @@ -1,4 +1,4 @@ -from cx_const import Light, MediaPlayer, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light, MediaPlayer from cx_core import LightController, MediaPlayerController @@ -8,21 +8,21 @@ class SmartThingsButtonLightController(LightController): No release command is sent. """ - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "single_click": Light.TOGGLE, "double_click": Light.ON_FULL_BRIGHTNESS, "hold": Light.SET_HALF_BRIGHTNESS, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: Light.TOGGLE, 1004: Light.ON_FULL_BRIGHTNESS, 1001: Light.SET_HALF_BRIGHTNESS, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "button_single_1_0_0_0": Light.TOGGLE, "button_double_2_0_0_0": Light.ON_FULL_BRIGHTNESS, @@ -36,21 +36,21 @@ class SmartThingsButtonMediaPlayerController(MediaPlayerController): No release command is sent. """ - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "single_click": MediaPlayer.PLAY_PAUSE, "double_click": MediaPlayer.NEXT_TRACK, "hold": MediaPlayer.PREVIOUS_TRACK, } - def get_deconz_actions_mapping(self) -> TypeActionsMapping: + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: return { 1002: MediaPlayer.PLAY_PAUSE, 1004: MediaPlayer.NEXT_TRACK, 1001: MediaPlayer.PREVIOUS_TRACK, } - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "button_single_1_0_0_0": MediaPlayer.PLAY_PAUSE, "button_double_2_0_0_0": MediaPlayer.NEXT_TRACK, diff --git a/apps/controllerx/cx_devices/sonoff.py b/apps/controllerx/cx_devices/sonoff.py index 3758b4ba..d4da22fc 100644 --- a/apps/controllerx/cx_devices/sonoff.py +++ b/apps/controllerx/cx_devices/sonoff.py @@ -1,10 +1,10 @@ -from cx_const import Light, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light from cx_core import LightController from cx_core.integration import EventData class SNZB01LightController(LightController): - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "toggle": Light.TOGGLE, # single click "on": Light.ON_FULL_BRIGHTNESS, # double click diff --git a/apps/controllerx/cx_devices/terncy.py b/apps/controllerx/cx_devices/terncy.py index f6142c81..dd4c4571 100644 --- a/apps/controllerx/cx_devices/terncy.py +++ b/apps/controllerx/cx_devices/terncy.py @@ -1,10 +1,10 @@ -from cx_const import Light, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light from cx_core import LightController from cx_core.integration import EventData class TerncyPP01LightController(LightController): - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "button_single": Light.TOGGLE, "button_double": Light.ON_FULL_BRIGHTNESS, @@ -18,7 +18,7 @@ def get_zha_action(self, data: EventData) -> str: class TerncySD01LightController(LightController): - def get_zha_actions_mapping(self) -> TypeActionsMapping: + def get_zha_actions_mapping(self) -> DefaultActionsMapping: return { "button_single": Light.TOGGLE, "button_double": Light.ON_FULL_BRIGHTNESS, diff --git a/apps/controllerx/cx_devices/trust.py b/apps/controllerx/cx_devices/trust.py index 5b3d3619..ddeb3d45 100644 --- a/apps/controllerx/cx_devices/trust.py +++ b/apps/controllerx/cx_devices/trust.py @@ -1,4 +1,4 @@ -from cx_const import Light, MediaPlayer, TypeActionsMapping +from cx_const import DefaultActionsMapping, Light, MediaPlayer from cx_core import LightController, MediaPlayerController @@ -7,7 +7,7 @@ class ZYCT202LightController(LightController): This controller does not send hold action for on/off """ - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on": Light.ON, "up-press": Light.HOLD_BRIGHTNESS_UP, @@ -18,7 +18,7 @@ def get_z2m_actions_mapping(self) -> TypeActionsMapping: class ZYCT202MediaPlayerController(MediaPlayerController): - def get_z2m_actions_mapping(self) -> TypeActionsMapping: + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: return { "on": MediaPlayer.PLAY_PAUSE, "up-press": MediaPlayer.HOLD_VOLUME_UP, diff --git a/setup.cfg b/setup.cfg index c0cfe0bf..e45a824a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,6 +16,7 @@ no_implicit_optional = True [tool:pytest] mock_use_standalone_module = true +timeout = 5 [report] exclude_lines = diff --git a/tests/conftest.py b/tests/conftest.py index 5b95a790..fc88f32d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,14 @@ import asyncio +from typing import Optional import appdaemon.plugins.hass.hassapi as hass # type: ignore -import appdaemon.plugins.mqtt.mqttapi as mqtt # type: ignore +import appdaemon.plugins.mqtt.mqttapi as mqtt import pytest from _pytest.monkeypatch import MonkeyPatch from cx_core import LightController +from cx_core.action_type.base import ActionType # type: ignore from cx_core.controller import Controller +from cx_core.integration import EventData from tests.test_utils import fake_fn @@ -52,3 +55,13 @@ def fake_type_controller() -> LightController: c = LightController() # type: ignore c.args = {} return c + + +class FakeActionType(ActionType): + async def run(self, extra: Optional[EventData] = None) -> None: + return None + + +@pytest.fixture +def fake_action_type(fake_controller: Controller) -> ActionType: + return FakeActionType(fake_controller, {}) diff --git a/tests/integ_tests/action-types/arrow_left_click_test.yaml b/tests/integ_tests/action-types/arrow_left_click_test.yaml new file mode 100644 index 00000000..477e3b8c --- /dev/null +++ b/tests/integ_tests/action-types/arrow_left_click_test.yaml @@ -0,0 +1,8 @@ +entity_state_attributes: + supported_features: 191 +entity_state: "off" +fired_actions: [arrow_left_click] +expected_calls: +- service: scene/turn_on + data: + entity_id: scene.my_scene diff --git a/tests/integ_tests/action-types/arrow_left_double_click_test.yaml b/tests/integ_tests/action-types/arrow_left_double_click_test.yaml new file mode 100644 index 00000000..37c31ebc --- /dev/null +++ b/tests/integ_tests/action-types/arrow_left_double_click_test.yaml @@ -0,0 +1,8 @@ +entity_state_attributes: + supported_features: 191 +entity_state: "off" +fired_actions: [arrow_left_click, 0.05, arrow_left_click] +expected_calls: +- service: scene/turn_on + data: + entity_id: scene.my_other_scene diff --git a/tests/integ_tests/action-types/arrow_right_click_test.yaml b/tests/integ_tests/action-types/arrow_right_click_test.yaml new file mode 100644 index 00000000..f655452c --- /dev/null +++ b/tests/integ_tests/action-types/arrow_right_click_test.yaml @@ -0,0 +1,9 @@ +entity_state_attributes: + supported_features: 191 +entity_state: "off" +fired_actions: [arrow_right_click] +expected_calls: +- service: my_service + data: + attr1: 42 + attr2: foo diff --git a/tests/integ_tests/action-types/brightness_down_click_test.yaml b/tests/integ_tests/action-types/brightness_down_click_test.yaml new file mode 100644 index 00000000..8207a174 --- /dev/null +++ b/tests/integ_tests/action-types/brightness_down_click_test.yaml @@ -0,0 +1,15 @@ +entity_state_attributes: + supported_features: 191 +entity_state: "off" +fired_actions: [brightness_down_click] +expected_calls: + - service: light/toggle + data: + entity_id: light.bedroom + - service: light/toggle + data: + entity_id: light.bedroom + - service: scene/turn_on + data: + entity_id: scene.my_other_scene + - service: my_other_service diff --git a/tests/integ_tests/action-types/brightness_up_click_test.yaml b/tests/integ_tests/action-types/brightness_up_click_test.yaml new file mode 100644 index 00000000..7b02386e --- /dev/null +++ b/tests/integ_tests/action-types/brightness_up_click_test.yaml @@ -0,0 +1,8 @@ +entity_state_attributes: + supported_features: 191 +entity_state: "off" +fired_actions: [brightness_up_click] +expected_calls: +- service: light/toggle + data: + entity_id: light.bedroom diff --git a/tests/integ_tests/action-types/config.yaml b/tests/integ_tests/action-types/config.yaml new file mode 100644 index 00000000..54e8e914 --- /dev/null +++ b/tests/integ_tests/action-types/config.yaml @@ -0,0 +1,28 @@ +livingroom_controller: + module: controllerx + class: E1810Controller + controller: sensor.livingroom_controller_action + integration: z2m + light: light.bedroom + mapping: + toggle: toggle + brightness_up_click: + action: toggle + arrow_left_click: + scene: scene.my_scene + arrow_left_click$2: + service: scene.turn_on + entity_id: scene.my_other_scene + data: + entity_id: scene.not_called + arrow_right_click: + service: my_service + data: + attr1: 42 + attr2: foo + brightness_down_click: + - toggle + - action: toggle + - delay: 1 + - scene: scene.my_other_scene + - service: my_other_service diff --git a/tests/integ_tests/action-types/toggle_test.yaml b/tests/integ_tests/action-types/toggle_test.yaml new file mode 100644 index 00000000..36154d7c --- /dev/null +++ b/tests/integ_tests/action-types/toggle_test.yaml @@ -0,0 +1,8 @@ +entity_state_attributes: + supported_features: 191 +entity_state: "off" +fired_actions: [toggle] +expected_calls: +- service: light/toggle + data: + entity_id: light.bedroom diff --git a/tests/integ_tests/integ_test.py b/tests/integ_tests/integ_test.py index 26211a26..42842542 100644 --- a/tests/integ_tests/integ_test.py +++ b/tests/integ_tests/integ_test.py @@ -5,6 +5,7 @@ import pytest import yaml +from appdaemon.plugins.hass.hassapi import Hass # type: ignore from pytest_mock.plugin import MockerFixture from tests.test_utils import get_controller @@ -59,7 +60,7 @@ async def test_integ_configs( fake_entity_states = get_fake_entity_states(entity_state, entity_state_attributes) mocker.patch.object(controller, "get_entity_state", fake_entity_states) - call_service_stub = mocker.patch.object(controller, "call_service") + call_service_stub = mocker.patch.object(Hass, "call_service") await controller.initialize() for idx, action in enumerate(fired_actions): @@ -81,6 +82,7 @@ async def test_integ_configs( await asyncio.wait(pending) assert call_service_stub.call_count == expected_calls_count calls = [ - mocker.call(call["service"], **call.get("data", {})) for call in expected_calls + mocker.call(controller, call["service"], **call.get("data", {})) + for call in expected_calls ] call_service_stub.assert_has_calls(calls) diff --git a/tests/test_utils.py b/tests/test_utils.py index 73e3c47b..caf4abc4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -17,8 +17,8 @@ class IntegrationMock: def __init__(self, name: str, controller: "Controller", mocker: MockerFixture): self.name = name self.controller = controller - self.get_actions_mapping = MagicMock( - name="get_actions_mapping", return_value={} + self.get_default_actions_mapping = MagicMock( + name="get_default_actions_mapping", return_value={} ) self.listen_changes = mocker.stub(name="listen_changes") diff --git a/tests/unit_tests/cx_core/action-types/predefined_action_type_test.py b/tests/unit_tests/cx_core/action-types/predefined_action_type_test.py new file mode 100644 index 00000000..92be8dfd --- /dev/null +++ b/tests/unit_tests/cx_core/action-types/predefined_action_type_test.py @@ -0,0 +1,21 @@ +import pytest +from cx_const import ActionFunctionWithParams, TypeAction +from cx_core.action_type.predefined_action_type import _get_action + +from tests.test_utils import fake_fn + + +@pytest.mark.parametrize( + "test_input, expected", + [ + (fake_fn, (fake_fn, tuple())), + ((fake_fn, tuple()), (fake_fn, tuple())), + ((fake_fn, ("test",)), (fake_fn, ("test",))), + ], +) +def test_get_action( + test_input: TypeAction, + expected: ActionFunctionWithParams, +): + output = _get_action(test_input) + assert output == expected diff --git a/tests/unit_tests/cx_core/controller_test.py b/tests/unit_tests/cx_core/controller_test.py index 5ba8afdb..d588ae0f 100644 --- a/tests/unit_tests/cx_core/controller_test.py +++ b/tests/unit_tests/cx_core/controller_test.py @@ -1,10 +1,12 @@ from collections import defaultdict from typing import Any, Dict, List, Optional, Union -import appdaemon.plugins.hass.hassapi as hass # type: ignore +import appdaemon.plugins.hass.hassapi as hass import pytest -from cx_const import ActionEvent, ActionFunction, ActionsMapping, TypeAction +from cx_const import ActionEvent from cx_core import integration as integration_module +from cx_core.action_type import ActionsMapping +from cx_core.action_type.base import ActionType # type: ignore from cx_core.controller import Controller, action from pytest_mock.plugin import MockerFixture @@ -139,7 +141,7 @@ async def test_initialize( error_expected: bool, ): actions = {action: action for action in actions_input} - type_actions = {action: lambda: None for action in actions_input} + predefined_actions = {action: lambda: None for action in actions_input} sut_before_init.args["controller"] = controller_input integration_mock = IntegrationMock(INTEGRATION_TEST_NAME, sut_before_init, mocker) mocker.patch.object( @@ -149,11 +151,17 @@ async def test_initialize( sut_before_init.args["actions"] = included_actions if excluded_actions: sut_before_init.args["excluded_actions"] = excluded_actions - mocker.patch.object(sut_before_init, "get_actions_mapping", return_value=actions) mocker.patch.object( - sut_before_init, "get_type_actions_mapping", return_value=type_actions + sut_before_init, "get_default_actions_mapping", return_value=actions + ) + mocker.patch.object( + sut_before_init, + "get_predefined_actions_mapping", + return_value=predefined_actions, + ) + get_default_actions_mapping = mocker.spy( + sut_before_init, "get_default_actions_mapping" ) - get_actions_mapping = mocker.spy(sut_before_init, "get_actions_mapping") # SUT with wrap_exetuction(error_expected=error_expected, exception=ValueError): @@ -161,7 +169,7 @@ async def test_initialize( # Checks if not error_expected: - get_actions_mapping.assert_called_once() + get_default_actions_mapping.assert_called_once() for controller_id in controller_input: integration_mock.listen_changes.assert_any_call(controller_id) assert integration_mock.listen_changes.call_count == len(controller_input) @@ -190,15 +198,19 @@ async def test_merge_mapping( ): actions_input = ["action1", "action2", "action3"] actions = {action: action for action in actions_input} - type_actions = {action: lambda: None for action in actions_input} + predefined_actions = {action: lambda: None for action in actions_input} if mapping: sut_before_init.args["mapping"] = {item: item for item in mapping} if merge_mapping: sut_before_init.args["merge_mapping"] = {item: item for item in merge_mapping} - mocker.patch.object(sut_before_init, "get_actions_mapping", return_value=actions) mocker.patch.object( - sut_before_init, "get_type_actions_mapping", return_value=type_actions + sut_before_init, "get_default_actions_mapping", return_value=actions + ) + mocker.patch.object( + sut_before_init, + "get_predefined_actions_mapping", + return_value=predefined_actions, ) # SUT @@ -245,9 +257,13 @@ def test_get_list( ], ) def test_get_multiple_click_actions( - sut: Controller, mapping: List[ActionEvent], expected: List[str] + fake_action_type: ActionType, + sut: Controller, + mapping: List[ActionEvent], + expected: List[str], ): - output = sut.get_multiple_click_actions({key: "action" for key in mapping}) + actions_mapping: ActionsMapping = {key: [fake_action_type] for key in mapping} + output = sut.get_multiple_click_actions(actions_mapping) assert output == set(expected) @@ -305,23 +321,27 @@ def test_check_ad_version_throwing_error(sut: Controller, mocker: MockerFixture) assert str(e.value) == "Please upgrade to AppDaemon 4.x" -def test_get_actions_mapping_happyflow(sut, monkeypatch, mocker): +def test_get_default_actions_mapping_happyflow(sut, monkeypatch, mocker): integration_mock = IntegrationMock("integration-test", sut, mocker) monkeypatch.setattr( - integration_mock, "get_actions_mapping", lambda: "this_is_a_mapping" + integration_mock, "get_default_actions_mapping", lambda: "this_is_a_mapping" ) - mapping = sut.get_actions_mapping(integration_mock) + mapping = sut.get_default_actions_mapping(integration_mock) assert mapping == "this_is_a_mapping" -def test_get_actions_mapping_throwing_error(sut: Controller, mocker: MockerFixture): +def test_get_default_actions_mapping_throwing_error( + sut: Controller, mocker: MockerFixture +): integration_mock = IntegrationMock("integration-test", sut, mocker) - mocker.patch.object(integration_mock, "get_actions_mapping", return_value=None) + mocker.patch.object( + integration_mock, "get_default_actions_mapping", return_value=None + ) with pytest.raises(ValueError) as e: - sut.get_actions_mapping(integration_mock) # type: ignore + sut.get_default_actions_mapping(integration_mock) # type: ignore assert str(e.value) == "This controller does not support integration-test." @@ -344,11 +364,14 @@ async def test_handle_action( action_called_times: int, action_delta: int, expected_calls: int, + fake_action_type: ActionType, ): sut.action_delta = action_delta sut.action_times = defaultdict(lambda: 0) - actions_mapping: ActionsMapping = {action: fake_fn() for action in actions_input} + actions_mapping: ActionsMapping = { + action: [fake_action_type] for action in actions_input + } sut.actions_mapping = actions_mapping call_action_patch = mocker.patch.object(sut, "call_action") @@ -380,7 +403,6 @@ async def test_call_action( action_timer_callback_called: bool, ): action_key = "test" - sut.actions_key_mapping = {"test": "test_action"} sut.action_delay = {action_key: delay} action_delay_handles: Dict[ActionEvent, Optional[float]] = {action_key: handle} sut.action_delay_handles = action_delay_handles @@ -408,35 +430,6 @@ async def test_call_action( ) -@pytest.mark.parametrize( - "test_input, expected, error_expected", - [ - (fake_fn, (fake_fn,), False), - ((fake_fn,), (fake_fn,), False), - ((fake_fn, "test"), (fake_fn, "test"), False), - ("not-list-or-function", (), True), - ], -) -def test_get_action( - sut: Controller, - test_input: TypeAction, - expected: ActionFunction, - error_expected: bool, -): - with wrap_exetuction( - error_expected=error_expected, exception=ValueError - ) as err_info: - output = sut.get_action(test_input) - - if err_info is not None: - assert ( - str(err_info.value) - == "The action value from the action mapping should be a list or a function" - ) - else: - assert output == expected - - @pytest.mark.parametrize( "service, attributes", [("test_service", {"attr1": 0.0, "attr2": "test"}), ("test_service", {})], diff --git a/tests/unit_tests/cx_core/custom_controller_test.py b/tests/unit_tests/cx_core/custom_controller_test.py index f59dea90..fbcbece7 100644 --- a/tests/unit_tests/cx_core/custom_controller_test.py +++ b/tests/unit_tests/cx_core/custom_controller_test.py @@ -2,10 +2,10 @@ import pytest from _pytest.monkeypatch import MonkeyPatch -from cx_const import TypeActionsMapping +from appdaemon.plugins.hass.hassapi import Hass # type: ignore +from cx_const import PredefinedActionsMapping from cx_core import ( CallServiceController, - Controller, CoverController, LightController, MediaPlayerController, @@ -68,7 +68,7 @@ async def test_custom_controllers( monkeypatch: MonkeyPatch, mocker: MockerFixture, custom_cls: Type[TypeController], - mapping: TypeActionsMapping, + mapping: PredefinedActionsMapping, action_input: str, mock_function: str, expected_calls: int, @@ -156,7 +156,7 @@ async def test_call_service_controller( async def fake_call_service(self, service, **data): call_service_stub(service, **data) - monkeypatch.setattr(Controller, "call_service", fake_call_service) + monkeypatch.setattr(Hass, "call_service", fake_call_service) # SUT await sut.initialize() diff --git a/tests/unit_tests/cx_core/type/type_test.py b/tests/unit_tests/cx_core/type/type_test.py index 8b2cdee2..f7aff6c2 100644 --- a/tests/unit_tests/cx_core/type/type_test.py +++ b/tests/unit_tests/cx_core/type/type_test.py @@ -1,7 +1,7 @@ from typing import Type import pytest -from cx_const import ActionsMapping +from cx_const import PredefinedActionsMapping from cx_core import type as type_module from cx_core.type_controller import TypeController from pytest_mock.plugin import MockerFixture @@ -9,7 +9,7 @@ from tests.test_utils import get_classes -def check_mapping(mapping: ActionsMapping) -> None: +def check_mapping(mapping: PredefinedActionsMapping) -> None: if mapping is None: return for v in mapping.values(): @@ -31,10 +31,9 @@ def check_mapping(mapping: ActionsMapping) -> None: @pytest.mark.parametrize("controller_type", controller_types) -def test_type_actions_mapping( +def test_predefined_actions_mapping( mocker: MockerFixture, controller_type: Type[TypeController] ): controller = controller_type() # type: ignore - # mocker.patch.object(TypeController, "initialize") - mappings = controller.get_type_actions_mapping() + mappings = controller.get_predefined_actions_mapping() check_mapping(mappings) diff --git a/tests/unit_tests/cx_devices/devices_test.py b/tests/unit_tests/cx_devices/devices_test.py index 250fcb02..5abdca4f 100644 --- a/tests/unit_tests/cx_devices/devices_test.py +++ b/tests/unit_tests/cx_devices/devices_test.py @@ -2,14 +2,14 @@ import cx_devices as devices_module import pytest -from cx_const import ActionEvent, TypeActionsMapping +from cx_const import ActionEvent, DefaultActionsMapping from cx_core import Controller, ReleaseHoldController from tests.test_utils import get_classes, get_controller def check_mapping( - mapping: Optional[TypeActionsMapping], + mapping: Optional[DefaultActionsMapping], all_possible_actions: KeysView[ActionEvent], device: Controller, ) -> None: @@ -54,9 +54,9 @@ def test_devices(device_class: Type[Controller]): device_from_controllerx is not None ), f"'{device_class.__name__}' not importable from controllerx.py" - type_actions_mapping = device.get_type_actions_mapping() - possible_actions = type_actions_mapping.keys() - integration_mappings_funcs: List[Callable[[], Optional[TypeActionsMapping]]] = [ + predefined_actions_mapping = device.get_predefined_actions_mapping() + possible_actions = predefined_actions_mapping.keys() + integration_mappings_funcs: List[Callable[[], Optional[DefaultActionsMapping]]] = [ device.get_z2m_actions_mapping, device.get_deconz_actions_mapping, device.get_zha_actions_mapping,