forked from platypush/platypush
Migrated zwave.mqtt integration.
Merged the zwave.mqtt plugin with the listener and removed the leftovers of the legacy zwave plugin.
This commit is contained in:
parent
52cb1afb4b
commit
2fcd623c51
6 changed files with 443 additions and 2814 deletions
File diff suppressed because it is too large
Load diff
|
@ -1,911 +0,0 @@
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from typing import Any, Dict, Optional, List, Union
|
|
||||||
|
|
||||||
from platypush.entities import (
|
|
||||||
DimmerEntityManager,
|
|
||||||
EnumSwitchEntityManager,
|
|
||||||
LightEntityManager,
|
|
||||||
SensorEntityManager,
|
|
||||||
SwitchEntityManager,
|
|
||||||
)
|
|
||||||
from platypush.plugins import Plugin, action
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveBasePlugin(
|
|
||||||
DimmerEntityManager,
|
|
||||||
EnumSwitchEntityManager,
|
|
||||||
LightEntityManager,
|
|
||||||
SensorEntityManager,
|
|
||||||
SwitchEntityManager,
|
|
||||||
Plugin,
|
|
||||||
ABC,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Base class for Z-Wave plugins.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def start_network(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def stop_network(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def status(self) -> Dict[str, Any]: # pylint: disable=arguments-differ
|
|
||||||
"""
|
|
||||||
Get the status of the controller.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def add_node(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Start the inclusion process to add a node to the network.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def remove_node(self):
|
|
||||||
"""
|
|
||||||
Remove a node from the network.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def remove_failed_node(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Remove a failed node from the network.
|
|
||||||
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def replace_failed_node(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Replace a failed node on the network.
|
|
||||||
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def replication_send(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Send node information from the primary to the secondary controller.
|
|
||||||
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def request_network_update(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Request a network update to a node.
|
|
||||||
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def request_node_neighbour_update(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Request a neighbours list update to a node.
|
|
||||||
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_nodes(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Get the nodes associated to the network.
|
|
||||||
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_node_stats(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Get the statistics of a node on the network.
|
|
||||||
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_node_name(
|
|
||||||
self,
|
|
||||||
new_name: str,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Rename a node on the network.
|
|
||||||
|
|
||||||
:param new_name: New name for the node.
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by current node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_node_product_name(
|
|
||||||
self,
|
|
||||||
product_name: str,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Set the product name of a node.
|
|
||||||
|
|
||||||
:param product_name: Product name.
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by current node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_node_manufacturer_name(
|
|
||||||
self,
|
|
||||||
manufacturer_name: str,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Set the manufacturer name of a node.
|
|
||||||
|
|
||||||
:param manufacturer_name: Manufacturer name.
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by current node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_node_location(
|
|
||||||
self,
|
|
||||||
location: str,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Set the location of a node.
|
|
||||||
|
|
||||||
:param location: Node location.
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by current node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def cancel_command(self):
|
|
||||||
"""
|
|
||||||
Cancel the current running command.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def kill_command(self):
|
|
||||||
"""
|
|
||||||
Immediately terminate any running command on the controller and release the lock.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_controller_name(self, name: str, **kwargs):
|
|
||||||
"""
|
|
||||||
Set the name of the controller on the network.
|
|
||||||
|
|
||||||
:param name: New controller name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_capabilities(self, **kwargs) -> List[str]:
|
|
||||||
"""
|
|
||||||
Get the capabilities of the controller.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def receive_configuration(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Receive the configuration from the primary controller on the network. Requires a primary controller active.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def transfer_primary_role(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Add a new controller to the network and make it the primary.
|
|
||||||
The existing primary will become a secondary controller.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def heal(self, refresh_routes: bool = False, **kwargs):
|
|
||||||
"""
|
|
||||||
Heal network by requesting nodes rediscover their neighbors.
|
|
||||||
|
|
||||||
:param refresh_routes: Whether to perform return routes initialization (default: ``False``).
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def switch_all(self, state: bool, **kwargs):
|
|
||||||
"""
|
|
||||||
Switch all the connected devices on/off.
|
|
||||||
|
|
||||||
:param state: True (switch on) or False (switch off).
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def test(self, count: int = 1, **kwargs):
|
|
||||||
"""
|
|
||||||
Send a number of test messages to every node and record results.
|
|
||||||
|
|
||||||
:param count: The number of test messages to send.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_value(
|
|
||||||
self,
|
|
||||||
value_id: Optional[int] = None,
|
|
||||||
id_on_network: Optional[str] = None,
|
|
||||||
value_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Get a value on the network.
|
|
||||||
|
|
||||||
:param value_id: Select by value_id.
|
|
||||||
:param id_on_network: Select value by id_on_network.
|
|
||||||
:param value_label: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_id: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_name: Select value by [node_id/node_name, value_label]
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_value(
|
|
||||||
self,
|
|
||||||
data,
|
|
||||||
value_id: Optional[int] = None,
|
|
||||||
id_on_network: Optional[str] = None,
|
|
||||||
value_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Set a value.
|
|
||||||
|
|
||||||
:param data: Data to set for the value.
|
|
||||||
:param value_id: Select value by value_id.
|
|
||||||
:param id_on_network: Select value by id_on_network.
|
|
||||||
:param value_label: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_id: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_name: Select value by [node_id/node_name, value_label]
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@action
|
|
||||||
def set(self, entity: str, value: Any, **kwargs):
|
|
||||||
return self.set_value(
|
|
||||||
value_id=entity, id_on_network=entity, data=value, **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_value_label(
|
|
||||||
self,
|
|
||||||
new_label: str,
|
|
||||||
value_id: Optional[int] = None,
|
|
||||||
id_on_network: Optional[str] = None,
|
|
||||||
value_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Change the label/name of a value.
|
|
||||||
|
|
||||||
:param new_label: New value label.
|
|
||||||
:param value_id: Select value by value_id.
|
|
||||||
:param id_on_network: Select value by id_on_network.
|
|
||||||
:param value_label: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_id: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_name: Select value by [node_id/node_name, value_label]
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def node_add_value(
|
|
||||||
self,
|
|
||||||
value_id: Optional[int] = None,
|
|
||||||
id_on_network: Optional[str] = None,
|
|
||||||
value_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Add a value to a node.
|
|
||||||
|
|
||||||
:param value_id: Select value by value_id.
|
|
||||||
:param id_on_network: Select value by id_on_network.
|
|
||||||
:param value_label: Select value by label.
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def node_remove_value(
|
|
||||||
self,
|
|
||||||
value_id: Optional[int] = None,
|
|
||||||
id_on_network: Optional[str] = None,
|
|
||||||
value_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Remove a value from a node.
|
|
||||||
|
|
||||||
:param value_id: Select value by value_id.
|
|
||||||
:param id_on_network: Select value by id_on_network.
|
|
||||||
:param value_label: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def node_heal(
|
|
||||||
self,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
refresh_routes: bool = False,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Heal network node by requesting the node to rediscover their neighbours.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
:param refresh_routes: Whether to perform return routes initialization. (default: ``False``).
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def node_update_neighbours(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Ask a node to update its neighbours table.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def node_network_update(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Update the controller with network information.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def node_refresh_info(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Fetch up-to-date information about the node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_dimmers(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the dimmers on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_node_config(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the configuration values of a node or of all the nodes on the network.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_battery_levels(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the battery levels of a node or of all the nodes on the network.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_power_levels(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the power levels of this node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_bulbs(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the bulbs/LEDs on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_switches(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the switches on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_sensors(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the sensors on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_doorlocks(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the doorlocks on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_usercodes(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the usercodes on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_thermostats(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the thermostats on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_protections(
|
|
||||||
self, node_id: Optional[int] = None, node_name: Optional[str] = None, **kwargs
|
|
||||||
) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the protection-compatible devices on the network or associated to a node.
|
|
||||||
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_groups(self, **kwargs) -> Dict[int, Any]:
|
|
||||||
"""
|
|
||||||
Get the groups on the network.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_scenes(self, **kwargs) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Get the scenes configured on the network.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def create_scene(self, label: str, **kwargs):
|
|
||||||
"""
|
|
||||||
Create a new scene.
|
|
||||||
|
|
||||||
:param label: Scene label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def remove_scene(
|
|
||||||
self,
|
|
||||||
scene_id: Optional[int] = None,
|
|
||||||
scene_label: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Remove a scene.
|
|
||||||
|
|
||||||
:param scene_id: Select by scene_id.
|
|
||||||
:param scene_label: Select by scene label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def activate_scene(
|
|
||||||
self,
|
|
||||||
scene_id: Optional[int] = None,
|
|
||||||
scene_label: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Activate a scene.
|
|
||||||
|
|
||||||
:param scene_id: Select by scene_id.
|
|
||||||
:param scene_label: Select by scene label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def set_scene_label(
|
|
||||||
self,
|
|
||||||
new_label: str,
|
|
||||||
scene_id: Optional[int] = None,
|
|
||||||
scene_label: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Rename a scene/set the scene label.
|
|
||||||
|
|
||||||
:param new_label: New label.
|
|
||||||
:param scene_id: Select by scene_id.
|
|
||||||
:param scene_label: Select by current scene label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def scene_add_value(
|
|
||||||
self,
|
|
||||||
data: Optional[Any] = None,
|
|
||||||
value_id: Optional[int] = None,
|
|
||||||
id_on_network: Optional[str] = None,
|
|
||||||
value_label: Optional[str] = None,
|
|
||||||
scene_id: Optional[int] = None,
|
|
||||||
scene_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Add a value to a scene.
|
|
||||||
|
|
||||||
:param data: Data to set for the value (default: current value data).
|
|
||||||
:param value_id: Select value by value_id.
|
|
||||||
:param id_on_network: Select value by id_on_network.
|
|
||||||
:param value_label: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_id: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_name: Select value by [node_id/node_name, value_label]
|
|
||||||
:param scene_id: Select scene by scene_id.
|
|
||||||
:param scene_label: Select scene by scene label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def scene_remove_value(
|
|
||||||
self,
|
|
||||||
value_id: Optional[int] = None,
|
|
||||||
id_on_network: Optional[str] = None,
|
|
||||||
value_label: Optional[str] = None,
|
|
||||||
scene_id: Optional[int] = None,
|
|
||||||
scene_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Remove a value from a scene.
|
|
||||||
|
|
||||||
:param value_id: Select value by value_id.
|
|
||||||
:param id_on_network: Select value by id_on_network.
|
|
||||||
:param value_label: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_id: Select value by [node_id/node_name, value_label]
|
|
||||||
:param node_name: Select value by [node_id/node_name, value_label]
|
|
||||||
:param scene_id: Select scene by scene_id.
|
|
||||||
:param scene_label: Select scene by scene label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def get_scene_values(
|
|
||||||
self,
|
|
||||||
scene_id: Optional[int] = None,
|
|
||||||
scene_label: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
) -> dict:
|
|
||||||
"""
|
|
||||||
Get the values associated to a scene.
|
|
||||||
|
|
||||||
:param scene_id: Select by scene_id.
|
|
||||||
:param scene_label: Select by scene label.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def create_button(
|
|
||||||
self,
|
|
||||||
button_id: Union[int, str],
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Create a handheld button on a device. Only intended for bridge firmware controllers.
|
|
||||||
|
|
||||||
:param button_id: The ID of the button.
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by current node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def delete_button(
|
|
||||||
self,
|
|
||||||
button_id: Union[int, str],
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Delete a button association from a device. Only intended for bridge firmware controllers.
|
|
||||||
|
|
||||||
:param button_id: The ID of the button.
|
|
||||||
:param node_id: Filter by node_id.
|
|
||||||
:param node_name: Filter by current node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def add_node_to_group(
|
|
||||||
self,
|
|
||||||
group_index: Optional[int] = None,
|
|
||||||
group_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Add a node to a group.
|
|
||||||
|
|
||||||
:param group_index: Select group by group index.
|
|
||||||
:param group_label: Select group by group label.
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def remove_node_from_group(
|
|
||||||
self,
|
|
||||||
group_index: Optional[int] = None,
|
|
||||||
group_label: Optional[str] = None,
|
|
||||||
node_id: Optional[int] = None,
|
|
||||||
node_name: Optional[str] = None,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Remove a node from a group.
|
|
||||||
|
|
||||||
:param group_index: Select group by group index.
|
|
||||||
:param group_label: Select group by group label.
|
|
||||||
:param node_id: Select node by node_id.
|
|
||||||
:param node_name: Select node by node name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def create_new_primary(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Create a new primary controller on the network when the previous primary fails.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def hard_reset(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Perform a hard reset of the controller. It erases its network configuration settings.
|
|
||||||
The controller becomes a primary controller ready to add devices to a new network.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def soft_reset(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Perform a soft reset of the controller.
|
|
||||||
Resets a controller without erasing its network configuration settings.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def write_config(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Store the current configuration of the network to the user directory.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def on(self, device: str, *args, **kwargs): # pylint: disable=arguments-differ
|
|
||||||
"""
|
|
||||||
Turn on a switch on a device.
|
|
||||||
|
|
||||||
:param device: ``id_on_network`` of the value to be switched on.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def off(self, device: str, *args, **kwargs): # pylint: disable=arguments-differ
|
|
||||||
"""
|
|
||||||
Turn off a switch on a device.
|
|
||||||
|
|
||||||
:param device: ``id_on_network`` of the value to be switched off.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@action
|
|
||||||
def toggle(self, device: str, *args, **kwargs): # pylint: disable=arguments-differ
|
|
||||||
"""
|
|
||||||
Toggle a switch on a device.
|
|
||||||
|
|
||||||
:param device: ``id_on_network`` of the value to be toggled.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
|
|
@ -1,7 +0,0 @@
|
||||||
manifest:
|
|
||||||
events: {}
|
|
||||||
install:
|
|
||||||
pip:
|
|
||||||
- python-openzwave
|
|
||||||
package: platypush.plugins.zwave
|
|
||||||
type: plugin
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,197 +0,0 @@
|
||||||
import contextlib
|
|
||||||
import json
|
|
||||||
from queue import Queue, Empty
|
|
||||||
from typing import Optional, Type
|
|
||||||
|
|
||||||
from platypush.backend.mqtt import MqttBackend
|
|
||||||
from platypush.context import get_bus, get_plugin
|
|
||||||
|
|
||||||
from platypush.config import Config
|
|
||||||
from platypush.message.event.zwave import (
|
|
||||||
ZwaveEvent,
|
|
||||||
ZwaveNodeAddedEvent,
|
|
||||||
ZwaveValueChangedEvent,
|
|
||||||
ZwaveNodeRemovedEvent,
|
|
||||||
ZwaveNodeRenamedEvent,
|
|
||||||
ZwaveNodeReadyEvent,
|
|
||||||
ZwaveNodeEvent,
|
|
||||||
ZwaveNodeAsleepEvent,
|
|
||||||
ZwaveNodeAwakeEvent,
|
|
||||||
ZwaveValueRemovedEvent,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveMqttListener(MqttBackend):
|
|
||||||
"""
|
|
||||||
Internal MQTT listener for ``zwave.mqtt`` events.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self._nodes = {}
|
|
||||||
self._groups = {}
|
|
||||||
self._last_state = None
|
|
||||||
self._events_queue = Queue()
|
|
||||||
self.events_topic = self.plugin.events_topic
|
|
||||||
self.server_info = {
|
|
||||||
'host': self.plugin.host,
|
|
||||||
'port': self.plugin.port or self._default_mqtt_port,
|
|
||||||
'tls_cafile': self.plugin.tls_cafile,
|
|
||||||
'tls_certfile': self.plugin.tls_certfile,
|
|
||||||
'tls_ciphers': self.plugin.tls_ciphers,
|
|
||||||
'tls_keyfile': self.plugin.tls_keyfile,
|
|
||||||
'tls_version': self.plugin.tls_version,
|
|
||||||
'username': self.plugin.username,
|
|
||||||
'password': self.plugin.password,
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners = [
|
|
||||||
{
|
|
||||||
**self.server_info,
|
|
||||||
'topics': [
|
|
||||||
self.plugin.events_topic + '/node/' + topic
|
|
||||||
for topic in [
|
|
||||||
'node_ready',
|
|
||||||
'node_sleep',
|
|
||||||
'node_value_updated',
|
|
||||||
'node_metadata_updated',
|
|
||||||
'node_wakeup',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__(
|
|
||||||
*args,
|
|
||||||
subscribe_default_topic=False,
|
|
||||||
listeners=listeners,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client_id = (
|
|
||||||
str(self.client_id or Config.get('device_id')) + '-zwavejs-mqtt'
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def plugin(self):
|
|
||||||
from platypush.plugins.zwave.mqtt import ZwaveMqttPlugin
|
|
||||||
|
|
||||||
plugin: Optional[ZwaveMqttPlugin] = get_plugin('zwave.mqtt')
|
|
||||||
assert plugin, 'The zwave.mqtt plugin is not configured'
|
|
||||||
return plugin
|
|
||||||
|
|
||||||
def _dispatch_event(
|
|
||||||
self,
|
|
||||||
event_type: Type[ZwaveEvent],
|
|
||||||
node: dict,
|
|
||||||
value: Optional[dict] = None,
|
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
node_id = node.get('id')
|
|
||||||
assert node_id is not None, 'No node ID specified'
|
|
||||||
|
|
||||||
# This is far from efficient (we are querying the latest version of the whole
|
|
||||||
# node for every event we receive), but this is the best we can do with recent
|
|
||||||
# versions of ZWaveJS that only transmit partial representations of the node and
|
|
||||||
# the value. The alternative would be to come up with a complex logic for merging
|
|
||||||
# cached and new values, with the risk of breaking back-compatibility with earlier
|
|
||||||
# implementations of zwavejs2mqtt.
|
|
||||||
node = kwargs['node'] = self.plugin.get_nodes(node_id).output # type: ignore
|
|
||||||
node_values = node.get('values', {})
|
|
||||||
|
|
||||||
if node and value:
|
|
||||||
# Infer the value_id structure if it's not provided on the event
|
|
||||||
value_id = value.get('id')
|
|
||||||
if value_id is None:
|
|
||||||
value_id = f"{value['commandClass']}-{value.get('endpoint', 0)}-{value['property']}"
|
|
||||||
if 'propertyKey' in value:
|
|
||||||
value_id += '-' + str(value['propertyKey'])
|
|
||||||
|
|
||||||
# Prepend the node_id to value_id if it's not available in node['values']
|
|
||||||
# (compatibility with more recent versions of ZwaveJS that don't provide
|
|
||||||
# the value_id on the events)
|
|
||||||
if value_id not in node_values:
|
|
||||||
value_id = f"{node_id}-{value_id}"
|
|
||||||
|
|
||||||
if value_id not in node_values:
|
|
||||||
self.logger.warning(
|
|
||||||
'value_id %s not found on node %s', value_id, node_id
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
value = kwargs['value'] = node_values[value_id]
|
|
||||||
|
|
||||||
if issubclass(event_type, ZwaveNodeEvent):
|
|
||||||
# If the node has been removed, remove it from the cache
|
|
||||||
if event_type == ZwaveNodeRemovedEvent:
|
|
||||||
self._nodes.pop(node_id, None)
|
|
||||||
# If this node_id wasn't cached before, then it's a new node
|
|
||||||
elif node_id not in self._nodes:
|
|
||||||
event_type = ZwaveNodeAddedEvent
|
|
||||||
# If the name has changed, we have a rename event
|
|
||||||
elif node['name'] != self._nodes[node_id]['name']:
|
|
||||||
event_type = ZwaveNodeRenamedEvent
|
|
||||||
# If nothing relevant has changed, update the cached instance and return
|
|
||||||
else:
|
|
||||||
self._nodes[node_id] = node
|
|
||||||
return
|
|
||||||
|
|
||||||
evt = event_type(**kwargs)
|
|
||||||
self._events_queue.put(evt)
|
|
||||||
|
|
||||||
if (
|
|
||||||
value
|
|
||||||
and issubclass(event_type, ZwaveValueChangedEvent)
|
|
||||||
and event_type != ZwaveValueRemovedEvent
|
|
||||||
):
|
|
||||||
self.plugin.publish_entities([kwargs['value']]) # type: ignore
|
|
||||||
|
|
||||||
def on_mqtt_message(self):
|
|
||||||
def handler(_, __, msg):
|
|
||||||
if not msg.topic.startswith(self.events_topic):
|
|
||||||
return
|
|
||||||
|
|
||||||
topic = (
|
|
||||||
msg.topic[(len(self.events_topic) + 1) :].split('/').pop() # noqa: E203
|
|
||||||
)
|
|
||||||
data = msg.payload.decode()
|
|
||||||
if not data:
|
|
||||||
return
|
|
||||||
|
|
||||||
with contextlib.suppress(ValueError, TypeError):
|
|
||||||
data = json.loads(data)['data']
|
|
||||||
|
|
||||||
try:
|
|
||||||
if topic == 'node_value_updated':
|
|
||||||
self._dispatch_event(
|
|
||||||
ZwaveValueChangedEvent, node=data[0], value=data[1]
|
|
||||||
)
|
|
||||||
elif topic == 'node_metadata_updated':
|
|
||||||
self._dispatch_event(ZwaveNodeEvent, node=data[0])
|
|
||||||
elif topic == 'node_sleep':
|
|
||||||
self._dispatch_event(ZwaveNodeAsleepEvent, node=data[0])
|
|
||||||
elif topic == 'node_wakeup':
|
|
||||||
self._dispatch_event(ZwaveNodeAwakeEvent, node=data[0])
|
|
||||||
elif topic == 'node_ready':
|
|
||||||
self._dispatch_event(ZwaveNodeReadyEvent, node=data[0])
|
|
||||||
elif topic == 'node_removed':
|
|
||||||
self._dispatch_event(ZwaveNodeRemovedEvent, node=data[0])
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.exception(e)
|
|
||||||
|
|
||||||
return handler
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
super().run()
|
|
||||||
self.logger.debug('Refreshing Z-Wave nodes')
|
|
||||||
self._nodes = self.plugin.get_nodes().output # type: ignore
|
|
||||||
|
|
||||||
while not self.should_stop():
|
|
||||||
try:
|
|
||||||
evt = self._events_queue.get(block=True, timeout=1)
|
|
||||||
except Empty:
|
|
||||||
continue
|
|
||||||
|
|
||||||
get_bus().post(evt)
|
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
|
97
platypush/plugins/zwave/mqtt/_state.py
Normal file
97
platypush/plugins/zwave/mqtt/_state.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, Optional, Tuple, Union
|
||||||
|
from threading import RLock
|
||||||
|
|
||||||
|
IdType = Union[str, int]
|
||||||
|
_CacheMap = Dict[IdType, dict]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Cache:
|
||||||
|
"""
|
||||||
|
Cache class for Z-Wave entities.
|
||||||
|
"""
|
||||||
|
|
||||||
|
by_id: _CacheMap = field(default_factory=dict)
|
||||||
|
by_name: _CacheMap = field(default_factory=dict)
|
||||||
|
_lock: RLock = field(default_factory=RLock)
|
||||||
|
|
||||||
|
def add(self, *objs: dict, overwrite: bool = False) -> None:
|
||||||
|
"""
|
||||||
|
Add objects to the cache.
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
if overwrite:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
for obj in objs:
|
||||||
|
obj_id = obj.get("id")
|
||||||
|
if obj_id:
|
||||||
|
self.by_id[obj_id] = obj
|
||||||
|
|
||||||
|
name = self._get_name(obj)
|
||||||
|
if name:
|
||||||
|
self.by_name[name] = obj
|
||||||
|
|
||||||
|
def get(
|
||||||
|
self,
|
||||||
|
obj: Optional[Union[IdType, Tuple[Optional[IdType], Optional[IdType]]]] = None,
|
||||||
|
default=None,
|
||||||
|
) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
Get an object from the cache, by ID or name/label.
|
||||||
|
"""
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(obj, tuple):
|
||||||
|
return self.get(obj[0], self.get(obj[1], default))
|
||||||
|
|
||||||
|
return self.by_id.get(obj, self.by_name.get(obj, default))
|
||||||
|
|
||||||
|
def __contains__(self, obj: Optional[IdType]) -> bool:
|
||||||
|
"""
|
||||||
|
Check if an object with the given ID/name is in the cache.
|
||||||
|
"""
|
||||||
|
return obj in self.by_id or obj in self.by_name
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""
|
||||||
|
Clear the cache.
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
self.by_id.clear()
|
||||||
|
self.by_name.clear()
|
||||||
|
|
||||||
|
def pop(self, obj_id: IdType, default=None) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
Remove and return an object from the cache.
|
||||||
|
"""
|
||||||
|
with self._lock:
|
||||||
|
obj = self.by_id.pop(obj_id, default)
|
||||||
|
if not obj:
|
||||||
|
return obj
|
||||||
|
|
||||||
|
name = self._get_name(obj)
|
||||||
|
if name:
|
||||||
|
self.by_name.pop(name, None)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_name(obj: dict) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
@return The name/label of an object.
|
||||||
|
"""
|
||||||
|
return obj.get("name", obj.get("label"))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class State:
|
||||||
|
"""
|
||||||
|
State of the Z-Wave network.
|
||||||
|
"""
|
||||||
|
|
||||||
|
nodes: Cache = field(default_factory=Cache)
|
||||||
|
values: Cache = field(default_factory=Cache)
|
||||||
|
scenes: Cache = field(default_factory=Cache)
|
||||||
|
groups: Cache = field(default_factory=Cache)
|
Loading…
Reference in a new issue