Remove deprecated (old) Z-Wave integration (#67221)
* Remove deprecated (old) Z-Wave integration * Mark migration tests as skip, for later cleanup
This commit is contained in:
parent
35261a9089
commit
2686be921c
106 changed files with 6 additions and 13535 deletions
|
@ -1464,7 +1464,6 @@ omit =
|
||||||
homeassistant/components/ziggo_mediabox_xl/media_player.py
|
homeassistant/components/ziggo_mediabox_xl/media_player.py
|
||||||
homeassistant/components/zoneminder/*
|
homeassistant/components/zoneminder/*
|
||||||
homeassistant/components/supla/*
|
homeassistant/components/supla/*
|
||||||
homeassistant/components/zwave/util.py
|
|
||||||
homeassistant/components/zwave_js/discovery.py
|
homeassistant/components/zwave_js/discovery.py
|
||||||
homeassistant/components/zwave_js/sensor.py
|
homeassistant/components/zwave_js/sensor.py
|
||||||
homeassistant/components/zwave_me/__init__.py
|
homeassistant/components/zwave_me/__init__.py
|
||||||
|
|
1
.github/workflows/wheels.yml
vendored
1
.github/workflows/wheels.yml
vendored
|
@ -148,7 +148,6 @@ jobs:
|
||||||
sed -i "s|# pySwitchmate|pySwitchmate|g" ${requirement_file}
|
sed -i "s|# pySwitchmate|pySwitchmate|g" ${requirement_file}
|
||||||
sed -i "s|# face_recognition|face_recognition|g" ${requirement_file}
|
sed -i "s|# face_recognition|face_recognition|g" ${requirement_file}
|
||||||
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
|
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
|
||||||
sed -i "s|# homeassistant-pyozw|homeassistant-pyozw|g" ${requirement_file}
|
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
|
|
|
@ -1179,8 +1179,6 @@ tests/components/zodiac/* @JulienTant
|
||||||
homeassistant/components/zone/* @home-assistant/core
|
homeassistant/components/zone/* @home-assistant/core
|
||||||
tests/components/zone/* @home-assistant/core
|
tests/components/zone/* @home-assistant/core
|
||||||
homeassistant/components/zoneminder/* @rohankapoorcom
|
homeassistant/components/zoneminder/* @rohankapoorcom
|
||||||
homeassistant/components/zwave/* @home-assistant/z-wave
|
|
||||||
tests/components/zwave/* @home-assistant/z-wave
|
|
||||||
homeassistant/components/zwave_js/* @home-assistant/z-wave
|
homeassistant/components/zwave_js/* @home-assistant/z-wave
|
||||||
tests/components/zwave_js/* @home-assistant/z-wave
|
tests/components/zwave_js/* @home-assistant/z-wave
|
||||||
homeassistant/components/zwave_me/* @lawfulchaos @Z-Wave-Me
|
homeassistant/components/zwave_me/* @lawfulchaos @Z-Wave-Me
|
||||||
|
|
|
@ -15,8 +15,7 @@ RUN \
|
||||||
-r homeassistant/requirements.txt --use-deprecated=legacy-resolver
|
-r homeassistant/requirements.txt --use-deprecated=legacy-resolver
|
||||||
COPY requirements_all.txt homeassistant/
|
COPY requirements_all.txt homeassistant/
|
||||||
RUN \
|
RUN \
|
||||||
sed -i "s|# homeassistant-pyozw|homeassistant-pyozw|g" homeassistant/requirements_all.txt \
|
pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
|
||||||
&& pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
|
|
||||||
-r homeassistant/requirements_all.txt --use-deprecated=legacy-resolver
|
-r homeassistant/requirements_all.txt --use-deprecated=legacy-resolver
|
||||||
|
|
||||||
## Setup Home Assistant Core
|
## Setup Home Assistant Core
|
||||||
|
|
|
@ -9,7 +9,7 @@ import voluptuous as vol
|
||||||
from homeassistant.components import frontend
|
from homeassistant.components import frontend
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.const import CONF_ID, EVENT_COMPONENT_LOADED
|
from homeassistant.const import CONF_ID, EVENT_COMPONENT_LOADED
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.setup import ATTR_COMPONENT
|
from homeassistant.setup import ATTR_COMPONENT
|
||||||
|
@ -29,7 +29,6 @@ SECTIONS = (
|
||||||
"script",
|
"script",
|
||||||
"scene",
|
"scene",
|
||||||
)
|
)
|
||||||
ON_DEMAND = ("zwave",)
|
|
||||||
ACTION_CREATE_UPDATE = "create_update"
|
ACTION_CREATE_UPDATE = "create_update"
|
||||||
ACTION_DELETE = "delete"
|
ACTION_DELETE = "delete"
|
||||||
|
|
||||||
|
@ -53,21 +52,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
key = f"{DOMAIN}.{panel_name}"
|
key = f"{DOMAIN}.{panel_name}"
|
||||||
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: key})
|
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: key})
|
||||||
|
|
||||||
@callback
|
|
||||||
def component_loaded(event):
|
|
||||||
"""Respond to components being loaded."""
|
|
||||||
panel_name = event.data.get(ATTR_COMPONENT)
|
|
||||||
if panel_name in ON_DEMAND:
|
|
||||||
hass.async_create_task(setup_panel(panel_name))
|
|
||||||
|
|
||||||
hass.bus.async_listen(EVENT_COMPONENT_LOADED, component_loaded)
|
|
||||||
|
|
||||||
tasks = [asyncio.create_task(setup_panel(panel_name)) for panel_name in SECTIONS]
|
tasks = [asyncio.create_task(setup_panel(panel_name)) for panel_name in SECTIONS]
|
||||||
|
|
||||||
for panel_name in ON_DEMAND:
|
|
||||||
if panel_name in hass.config.components:
|
|
||||||
tasks.append(asyncio.create_task(setup_panel(panel_name)))
|
|
||||||
|
|
||||||
if tasks:
|
if tasks:
|
||||||
await asyncio.wait(tasks)
|
await asyncio.wait(tasks)
|
||||||
|
|
||||||
|
|
|
@ -1,259 +0,0 @@
|
||||||
"""Provide configuration end points for Z-Wave."""
|
|
||||||
from collections import deque
|
|
||||||
from http import HTTPStatus
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from aiohttp.web import Response
|
|
||||||
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
|
||||||
from homeassistant.components.zwave import DEVICE_CONFIG_SCHEMA_ENTRY, const
|
|
||||||
import homeassistant.core as ha
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
from . import EditKeyBasedConfigView
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
CONFIG_PATH = "zwave_device_config.yaml"
|
|
||||||
OZW_LOG_FILENAME = "OZW_Log.txt"
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass):
|
|
||||||
"""Set up the Z-Wave config API."""
|
|
||||||
hass.http.register_view(
|
|
||||||
EditKeyBasedConfigView(
|
|
||||||
"zwave",
|
|
||||||
"device_config",
|
|
||||||
CONFIG_PATH,
|
|
||||||
cv.entity_id,
|
|
||||||
DEVICE_CONFIG_SCHEMA_ENTRY,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
hass.http.register_view(ZWaveNodeValueView)
|
|
||||||
hass.http.register_view(ZWaveNodeGroupView)
|
|
||||||
hass.http.register_view(ZWaveNodeConfigView)
|
|
||||||
hass.http.register_view(ZWaveUserCodeView)
|
|
||||||
hass.http.register_view(ZWaveLogView)
|
|
||||||
hass.http.register_view(ZWaveConfigWriteView)
|
|
||||||
hass.http.register_view(ZWaveProtectionView)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveLogView(HomeAssistantView):
|
|
||||||
"""View to read the ZWave log file."""
|
|
||||||
|
|
||||||
url = "/api/zwave/ozwlog"
|
|
||||||
name = "api:zwave:ozwlog"
|
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
|
||||||
async def get(self, request):
|
|
||||||
"""Retrieve the lines from ZWave log."""
|
|
||||||
try:
|
|
||||||
lines = int(request.query.get("lines", 0))
|
|
||||||
except ValueError:
|
|
||||||
return Response(text="Invalid datetime", status=HTTPStatus.BAD_REQUEST)
|
|
||||||
|
|
||||||
hass = request.app["hass"]
|
|
||||||
response = await hass.async_add_executor_job(self._get_log, hass, lines)
|
|
||||||
|
|
||||||
return Response(text="\n".join(response))
|
|
||||||
|
|
||||||
def _get_log(self, hass, lines):
|
|
||||||
"""Retrieve the logfile content."""
|
|
||||||
logfilepath = hass.config.path(OZW_LOG_FILENAME)
|
|
||||||
with open(logfilepath, encoding="utf8") as logfile:
|
|
||||||
data = (line.rstrip() for line in logfile)
|
|
||||||
if lines == 0:
|
|
||||||
loglines = list(data)
|
|
||||||
else:
|
|
||||||
loglines = deque(data, lines)
|
|
||||||
return loglines
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveConfigWriteView(HomeAssistantView):
|
|
||||||
"""View to save the ZWave configuration to zwcfg_xxxxx.xml."""
|
|
||||||
|
|
||||||
url = "/api/zwave/saveconfig"
|
|
||||||
name = "api:zwave:saveconfig"
|
|
||||||
|
|
||||||
@ha.callback
|
|
||||||
def post(self, request):
|
|
||||||
"""Save cache configuration to zwcfg_xxxxx.xml."""
|
|
||||||
hass = request.app["hass"]
|
|
||||||
if (network := hass.data.get(const.DATA_NETWORK)) is None:
|
|
||||||
return self.json_message(
|
|
||||||
"No Z-Wave network data found", HTTPStatus.NOT_FOUND
|
|
||||||
)
|
|
||||||
_LOGGER.info("Z-Wave configuration written to file")
|
|
||||||
network.write_config()
|
|
||||||
return self.json_message("Z-Wave configuration saved to file")
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveNodeValueView(HomeAssistantView):
|
|
||||||
"""View to return the node values."""
|
|
||||||
|
|
||||||
url = r"/api/zwave/values/{node_id:\d+}"
|
|
||||||
name = "api:zwave:values"
|
|
||||||
|
|
||||||
@ha.callback
|
|
||||||
def get(self, request, node_id):
|
|
||||||
"""Retrieve groups of node."""
|
|
||||||
nodeid = int(node_id)
|
|
||||||
hass = request.app["hass"]
|
|
||||||
values_list = hass.data[const.DATA_ENTITY_VALUES]
|
|
||||||
|
|
||||||
values_data = {}
|
|
||||||
# Return a list of values for this node that are used as a
|
|
||||||
# primary value for an entity
|
|
||||||
for entity_values in values_list:
|
|
||||||
if entity_values.primary.node.node_id != nodeid:
|
|
||||||
continue
|
|
||||||
|
|
||||||
values_data[entity_values.primary.value_id] = {
|
|
||||||
"label": entity_values.primary.label,
|
|
||||||
"index": entity_values.primary.index,
|
|
||||||
"instance": entity_values.primary.instance,
|
|
||||||
"poll_intensity": entity_values.primary.poll_intensity,
|
|
||||||
}
|
|
||||||
return self.json(values_data)
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveNodeGroupView(HomeAssistantView):
|
|
||||||
"""View to return the nodes group configuration."""
|
|
||||||
|
|
||||||
url = r"/api/zwave/groups/{node_id:\d+}"
|
|
||||||
name = "api:zwave:groups"
|
|
||||||
|
|
||||||
@ha.callback
|
|
||||||
def get(self, request, node_id):
|
|
||||||
"""Retrieve groups of node."""
|
|
||||||
nodeid = int(node_id)
|
|
||||||
hass = request.app["hass"]
|
|
||||||
network = hass.data.get(const.DATA_NETWORK)
|
|
||||||
if (node := network.nodes.get(nodeid)) is None:
|
|
||||||
return self.json_message("Node not found", HTTPStatus.NOT_FOUND)
|
|
||||||
groupdata = node.groups
|
|
||||||
groups = {}
|
|
||||||
for key, value in groupdata.items():
|
|
||||||
groups[key] = {
|
|
||||||
"associations": value.associations,
|
|
||||||
"association_instances": value.associations_instances,
|
|
||||||
"label": value.label,
|
|
||||||
"max_associations": value.max_associations,
|
|
||||||
}
|
|
||||||
return self.json(groups)
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveNodeConfigView(HomeAssistantView):
|
|
||||||
"""View to return the nodes configuration options."""
|
|
||||||
|
|
||||||
url = r"/api/zwave/config/{node_id:\d+}"
|
|
||||||
name = "api:zwave:config"
|
|
||||||
|
|
||||||
@ha.callback
|
|
||||||
def get(self, request, node_id):
|
|
||||||
"""Retrieve configurations of node."""
|
|
||||||
nodeid = int(node_id)
|
|
||||||
hass = request.app["hass"]
|
|
||||||
network = hass.data.get(const.DATA_NETWORK)
|
|
||||||
if (node := network.nodes.get(nodeid)) is None:
|
|
||||||
return self.json_message("Node not found", HTTPStatus.NOT_FOUND)
|
|
||||||
config = {}
|
|
||||||
for value in node.get_values(
|
|
||||||
class_id=const.COMMAND_CLASS_CONFIGURATION
|
|
||||||
).values():
|
|
||||||
config[value.index] = {
|
|
||||||
"label": value.label,
|
|
||||||
"type": value.type,
|
|
||||||
"help": value.help,
|
|
||||||
"data_items": value.data_items,
|
|
||||||
"data": value.data,
|
|
||||||
"max": value.max,
|
|
||||||
"min": value.min,
|
|
||||||
}
|
|
||||||
return self.json(config)
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveUserCodeView(HomeAssistantView):
|
|
||||||
"""View to return the nodes usercode configuration."""
|
|
||||||
|
|
||||||
url = r"/api/zwave/usercodes/{node_id:\d+}"
|
|
||||||
name = "api:zwave:usercodes"
|
|
||||||
|
|
||||||
@ha.callback
|
|
||||||
def get(self, request, node_id):
|
|
||||||
"""Retrieve usercodes of node."""
|
|
||||||
nodeid = int(node_id)
|
|
||||||
hass = request.app["hass"]
|
|
||||||
network = hass.data.get(const.DATA_NETWORK)
|
|
||||||
if (node := network.nodes.get(nodeid)) is None:
|
|
||||||
return self.json_message("Node not found", HTTPStatus.NOT_FOUND)
|
|
||||||
usercodes = {}
|
|
||||||
if not node.has_command_class(const.COMMAND_CLASS_USER_CODE):
|
|
||||||
return self.json(usercodes)
|
|
||||||
for value in node.get_values(class_id=const.COMMAND_CLASS_USER_CODE).values():
|
|
||||||
if value.genre != const.GENRE_USER:
|
|
||||||
continue
|
|
||||||
usercodes[value.index] = {
|
|
||||||
"code": value.data,
|
|
||||||
"label": value.label,
|
|
||||||
"length": len(value.data),
|
|
||||||
}
|
|
||||||
return self.json(usercodes)
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveProtectionView(HomeAssistantView):
|
|
||||||
"""View for the protection commandclass of a node."""
|
|
||||||
|
|
||||||
url = r"/api/zwave/protection/{node_id:\d+}"
|
|
||||||
name = "api:zwave:protection"
|
|
||||||
|
|
||||||
async def get(self, request, node_id):
|
|
||||||
"""Retrieve the protection commandclass options of node."""
|
|
||||||
nodeid = int(node_id)
|
|
||||||
hass = request.app["hass"]
|
|
||||||
network = hass.data.get(const.DATA_NETWORK)
|
|
||||||
|
|
||||||
def _fetch_protection():
|
|
||||||
"""Get protection data."""
|
|
||||||
if (node := network.nodes.get(nodeid)) is None:
|
|
||||||
return self.json_message("Node not found", HTTPStatus.NOT_FOUND)
|
|
||||||
protection_options = {}
|
|
||||||
if not node.has_command_class(const.COMMAND_CLASS_PROTECTION):
|
|
||||||
return self.json(protection_options)
|
|
||||||
protections = node.get_protections()
|
|
||||||
protection_options = {
|
|
||||||
"value_id": f"{list(protections)[0]:d}",
|
|
||||||
"selected": node.get_protection_item(list(protections)[0]),
|
|
||||||
"options": node.get_protection_items(list(protections)[0]),
|
|
||||||
}
|
|
||||||
return self.json(protection_options)
|
|
||||||
|
|
||||||
return await hass.async_add_executor_job(_fetch_protection)
|
|
||||||
|
|
||||||
async def post(self, request, node_id):
|
|
||||||
"""Change the selected option in protection commandclass."""
|
|
||||||
nodeid = int(node_id)
|
|
||||||
hass = request.app["hass"]
|
|
||||||
network = hass.data.get(const.DATA_NETWORK)
|
|
||||||
protection_data = await request.json()
|
|
||||||
|
|
||||||
def _set_protection():
|
|
||||||
"""Set protection data."""
|
|
||||||
node = network.nodes.get(nodeid)
|
|
||||||
selection = protection_data["selection"]
|
|
||||||
value_id = int(protection_data[const.ATTR_VALUE_ID])
|
|
||||||
if node is None:
|
|
||||||
return self.json_message("Node not found", HTTPStatus.NOT_FOUND)
|
|
||||||
if not node.has_command_class(const.COMMAND_CLASS_PROTECTION):
|
|
||||||
return self.json_message(
|
|
||||||
"No protection commandclass on this node", HTTPStatus.NOT_FOUND
|
|
||||||
)
|
|
||||||
state = node.set_protection(value_id, selection)
|
|
||||||
if not state:
|
|
||||||
return self.json_message(
|
|
||||||
"Protection setting did not complete", HTTPStatus.ACCEPTED
|
|
||||||
)
|
|
||||||
return self.json_message("Protection setting successfully set")
|
|
||||||
|
|
||||||
return await hass.async_add_executor_job(_set_protection)
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,106 +0,0 @@
|
||||||
"""Support for Z-Wave binary sensors."""
|
|
||||||
import datetime
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorEntity
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
from homeassistant.helpers.event import track_point_in_time
|
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
from . import ZWaveDeviceEntity, workaround
|
|
||||||
from .const import COMMAND_CLASS_SENSOR_BINARY
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave binary sensors from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_binary_sensor(binary_sensor):
|
|
||||||
"""Add Z-Wave binary sensor."""
|
|
||||||
async_add_entities([binary_sensor])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_binary_sensor", async_add_binary_sensor)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(values, **kwargs):
|
|
||||||
"""Create Z-Wave entity device."""
|
|
||||||
device_mapping = workaround.get_device_mapping(values.primary)
|
|
||||||
if device_mapping == workaround.WORKAROUND_NO_OFF_EVENT:
|
|
||||||
return ZWaveTriggerSensor(values, "motion")
|
|
||||||
|
|
||||||
if workaround.get_device_component_mapping(values.primary) == DOMAIN:
|
|
||||||
return ZWaveBinarySensor(values, None)
|
|
||||||
|
|
||||||
if values.primary.command_class == COMMAND_CLASS_SENSOR_BINARY:
|
|
||||||
return ZWaveBinarySensor(values, None)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveBinarySensor(BinarySensorEntity, ZWaveDeviceEntity):
|
|
||||||
"""Representation of a binary sensor within Z-Wave."""
|
|
||||||
|
|
||||||
def __init__(self, values, device_class):
|
|
||||||
"""Initialize the sensor."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self._sensor_type = device_class
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle data changes for node values."""
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if the binary sensor is on."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this sensor, from BinarySensorDeviceClass."""
|
|
||||||
return self._sensor_type
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveTriggerSensor(ZWaveBinarySensor):
|
|
||||||
"""Representation of a stateless sensor within Z-Wave."""
|
|
||||||
|
|
||||||
def __init__(self, values, device_class):
|
|
||||||
"""Initialize the sensor."""
|
|
||||||
super().__init__(values, device_class)
|
|
||||||
# Set default off delay to 60 sec
|
|
||||||
self.re_arm_sec = 60
|
|
||||||
self.invalidate_after = None
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle value changes for this entity's node."""
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
_LOGGER.debug("off_delay=%s", self.values.off_delay)
|
|
||||||
# Set re_arm_sec if off_delay is provided from the sensor
|
|
||||||
if self.values.off_delay:
|
|
||||||
_LOGGER.debug("off_delay.data=%s", self.values.off_delay.data)
|
|
||||||
self.re_arm_sec = self.values.off_delay.data * 8
|
|
||||||
# only allow this value to be true for re_arm secs
|
|
||||||
if not self.hass:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.invalidate_after = dt_util.utcnow() + datetime.timedelta(
|
|
||||||
seconds=self.re_arm_sec
|
|
||||||
)
|
|
||||||
track_point_in_time(
|
|
||||||
self.hass, self.async_update_ha_state, self.invalidate_after
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if movement has happened within the rearm time."""
|
|
||||||
return self._state and (
|
|
||||||
self.invalidate_after is None or self.invalidate_after > dt_util.utcnow()
|
|
||||||
)
|
|
|
@ -1,619 +0,0 @@
|
||||||
"""Support for Z-Wave climate devices."""
|
|
||||||
# Because we do not compile openzwave on CI
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from homeassistant.components.climate import ClimateEntity
|
|
||||||
from homeassistant.components.climate.const import (
|
|
||||||
ATTR_TARGET_TEMP_HIGH,
|
|
||||||
ATTR_TARGET_TEMP_LOW,
|
|
||||||
CURRENT_HVAC_COOL,
|
|
||||||
CURRENT_HVAC_FAN,
|
|
||||||
CURRENT_HVAC_HEAT,
|
|
||||||
CURRENT_HVAC_IDLE,
|
|
||||||
CURRENT_HVAC_OFF,
|
|
||||||
DOMAIN,
|
|
||||||
HVAC_MODE_AUTO,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_DRY,
|
|
||||||
HVAC_MODE_FAN_ONLY,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
PRESET_AWAY,
|
|
||||||
PRESET_BOOST,
|
|
||||||
PRESET_NONE,
|
|
||||||
SUPPORT_AUX_HEAT,
|
|
||||||
SUPPORT_FAN_MODE,
|
|
||||||
SUPPORT_PRESET_MODE,
|
|
||||||
SUPPORT_SWING_MODE,
|
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
|
||||||
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
|
||||||
)
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
|
|
||||||
from . import ZWaveDeviceEntity, const
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
CONF_NAME = "name"
|
|
||||||
DEFAULT_NAME = "Z-Wave Climate"
|
|
||||||
|
|
||||||
REMOTEC = 0x5254
|
|
||||||
REMOTEC_ZXT_120 = 0x8377
|
|
||||||
REMOTEC_ZXT_120_THERMOSTAT = (REMOTEC, REMOTEC_ZXT_120)
|
|
||||||
ATTR_OPERATING_STATE = "operating_state"
|
|
||||||
ATTR_FAN_STATE = "fan_state"
|
|
||||||
ATTR_FAN_ACTION = "fan_action"
|
|
||||||
AUX_HEAT_ZWAVE_MODE = "Aux Heat"
|
|
||||||
|
|
||||||
# Device is in manufacturer specific mode (e.g. setting the valve manually)
|
|
||||||
PRESET_MANUFACTURER_SPECIFIC = "Manufacturer Specific"
|
|
||||||
|
|
||||||
WORKAROUND_ZXT_120 = "zxt_120"
|
|
||||||
|
|
||||||
DEVICE_MAPPINGS = {REMOTEC_ZXT_120_THERMOSTAT: WORKAROUND_ZXT_120}
|
|
||||||
|
|
||||||
HVAC_STATE_MAPPINGS = {
|
|
||||||
"off": HVAC_MODE_OFF,
|
|
||||||
"heat": HVAC_MODE_HEAT,
|
|
||||||
"heat mode": HVAC_MODE_HEAT,
|
|
||||||
"heat (default)": HVAC_MODE_HEAT,
|
|
||||||
"furnace": HVAC_MODE_HEAT,
|
|
||||||
"fan only": HVAC_MODE_FAN_ONLY,
|
|
||||||
"dry air": HVAC_MODE_DRY,
|
|
||||||
"moist air": HVAC_MODE_DRY,
|
|
||||||
"cool": HVAC_MODE_COOL,
|
|
||||||
"heat_cool": HVAC_MODE_HEAT_COOL,
|
|
||||||
"auto": HVAC_MODE_HEAT_COOL,
|
|
||||||
"auto changeover": HVAC_MODE_HEAT_COOL,
|
|
||||||
}
|
|
||||||
|
|
||||||
MODE_SETPOINT_MAPPINGS = {
|
|
||||||
"off": (),
|
|
||||||
"heat": ("setpoint_heating",),
|
|
||||||
"cool": ("setpoint_cooling",),
|
|
||||||
"auto": ("setpoint_heating", "setpoint_cooling"),
|
|
||||||
"aux heat": ("setpoint_heating",),
|
|
||||||
"furnace": ("setpoint_furnace",),
|
|
||||||
"dry air": ("setpoint_dry_air",),
|
|
||||||
"moist air": ("setpoint_moist_air",),
|
|
||||||
"auto changeover": ("setpoint_auto_changeover",),
|
|
||||||
"heat econ": ("setpoint_eco_heating",),
|
|
||||||
"cool econ": ("setpoint_eco_cooling",),
|
|
||||||
"away": ("setpoint_away_heating", "setpoint_away_cooling"),
|
|
||||||
"full power": ("setpoint_full_power",),
|
|
||||||
# aliases found in xml configs
|
|
||||||
"comfort": ("setpoint_heating",),
|
|
||||||
"heat mode": ("setpoint_heating",),
|
|
||||||
"heat (default)": ("setpoint_heating",),
|
|
||||||
"dry floor": ("setpoint_dry_air",),
|
|
||||||
"heat eco": ("setpoint_eco_heating",),
|
|
||||||
"energy saving": ("setpoint_eco_heating",),
|
|
||||||
"energy heat": ("setpoint_eco_heating",),
|
|
||||||
"vacation": ("setpoint_away_heating", "setpoint_away_cooling"),
|
|
||||||
# for tests
|
|
||||||
"heat_cool": ("setpoint_heating", "setpoint_cooling"),
|
|
||||||
}
|
|
||||||
|
|
||||||
HVAC_CURRENT_MAPPINGS = {
|
|
||||||
"idle": CURRENT_HVAC_IDLE,
|
|
||||||
"heat": CURRENT_HVAC_HEAT,
|
|
||||||
"pending heat": CURRENT_HVAC_IDLE,
|
|
||||||
"heating": CURRENT_HVAC_HEAT,
|
|
||||||
"cool": CURRENT_HVAC_COOL,
|
|
||||||
"pending cool": CURRENT_HVAC_IDLE,
|
|
||||||
"cooling": CURRENT_HVAC_COOL,
|
|
||||||
"fan only": CURRENT_HVAC_FAN,
|
|
||||||
"vent / economiser": CURRENT_HVAC_FAN,
|
|
||||||
"off": CURRENT_HVAC_OFF,
|
|
||||||
}
|
|
||||||
|
|
||||||
PRESET_MAPPINGS = {
|
|
||||||
"away": PRESET_AWAY,
|
|
||||||
"full power": PRESET_BOOST,
|
|
||||||
"manufacturer specific": PRESET_MANUFACTURER_SPECIFIC,
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFAULT_HVAC_MODES = [
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_FAN_ONLY,
|
|
||||||
HVAC_MODE_DRY,
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODE_AUTO,
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave Climate device from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_climate(climate):
|
|
||||||
"""Add Z-Wave Climate Device."""
|
|
||||||
async_add_entities([climate])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_climate", async_add_climate)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(hass, values, **kwargs):
|
|
||||||
"""Create Z-Wave entity device."""
|
|
||||||
temp_unit = hass.config.units.temperature_unit
|
|
||||||
if values.primary.command_class == const.COMMAND_CLASS_THERMOSTAT_SETPOINT:
|
|
||||||
return ZWaveClimateSingleSetpoint(values, temp_unit)
|
|
||||||
if values.primary.command_class == const.COMMAND_CLASS_THERMOSTAT_MODE:
|
|
||||||
return ZWaveClimateMultipleSetpoint(values, temp_unit)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveClimateBase(ZWaveDeviceEntity, ClimateEntity):
|
|
||||||
"""Representation of a Z-Wave Climate device."""
|
|
||||||
|
|
||||||
def __init__(self, values, temp_unit):
|
|
||||||
"""Initialize the Z-Wave climate device."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self._target_temperature = None
|
|
||||||
self._target_temperature_range = (None, None)
|
|
||||||
self._current_temperature = None
|
|
||||||
self._hvac_action = None
|
|
||||||
self._hvac_list = None # [zwave_mode]
|
|
||||||
self._hvac_mapping = None # {ha_mode:zwave_mode}
|
|
||||||
self._hvac_mode = None # ha_mode
|
|
||||||
self._aux_heat = None
|
|
||||||
self._default_hvac_mode = None # ha_mode
|
|
||||||
self._preset_mapping = None # {ha_mode:zwave_mode}
|
|
||||||
self._preset_list = None # [zwave_mode]
|
|
||||||
self._preset_mode = None # ha_mode if exists, else zwave_mode
|
|
||||||
self._current_fan_mode = None
|
|
||||||
self._fan_modes = None
|
|
||||||
self._fan_action = None
|
|
||||||
self._current_swing_mode = None
|
|
||||||
self._swing_modes = None
|
|
||||||
self._unit = temp_unit
|
|
||||||
_LOGGER.debug("temp_unit is %s", self._unit)
|
|
||||||
self._zxt_120 = None
|
|
||||||
# Make sure that we have values for the key before converting to int
|
|
||||||
if self.node.manufacturer_id.strip() and self.node.product_id.strip():
|
|
||||||
specific_sensor_key = (
|
|
||||||
int(self.node.manufacturer_id, 16),
|
|
||||||
int(self.node.product_id, 16),
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
specific_sensor_key in DEVICE_MAPPINGS
|
|
||||||
and DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_ZXT_120
|
|
||||||
):
|
|
||||||
_LOGGER.debug("Remotec ZXT-120 Zwave Thermostat workaround")
|
|
||||||
self._zxt_120 = 1
|
|
||||||
self.update_properties()
|
|
||||||
|
|
||||||
def _mode(self) -> None:
|
|
||||||
"""Return thermostat mode Z-Wave value."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _current_mode_setpoints(self) -> tuple:
|
|
||||||
"""Return a tuple of current setpoint Z-Wave value(s)."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Return the list of supported features."""
|
|
||||||
support = SUPPORT_TARGET_TEMPERATURE
|
|
||||||
if self._hvac_list and HVAC_MODE_HEAT_COOL in self._hvac_list:
|
|
||||||
support |= SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
if self._preset_list and PRESET_AWAY in self._preset_list:
|
|
||||||
support |= SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
|
|
||||||
if self.values.fan_mode:
|
|
||||||
support |= SUPPORT_FAN_MODE
|
|
||||||
if self._zxt_120 == 1 and self.values.zxt_120_swing_mode:
|
|
||||||
support |= SUPPORT_SWING_MODE
|
|
||||||
if self._aux_heat:
|
|
||||||
support |= SUPPORT_AUX_HEAT
|
|
||||||
if self._preset_list:
|
|
||||||
support |= SUPPORT_PRESET_MODE
|
|
||||||
return support
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle the data changes for node values."""
|
|
||||||
# Operation Mode
|
|
||||||
self._update_operation_mode()
|
|
||||||
|
|
||||||
# Current Temp
|
|
||||||
self._update_current_temp()
|
|
||||||
|
|
||||||
# Fan Mode
|
|
||||||
self._update_fan_mode()
|
|
||||||
|
|
||||||
# Swing mode
|
|
||||||
self._update_swing_mode()
|
|
||||||
|
|
||||||
# Set point
|
|
||||||
self._update_target_temp()
|
|
||||||
|
|
||||||
# Operating state
|
|
||||||
self._update_operating_state()
|
|
||||||
|
|
||||||
# Fan operating state
|
|
||||||
self._update_fan_state()
|
|
||||||
|
|
||||||
def _update_operation_mode(self):
|
|
||||||
"""Update hvac and preset modes."""
|
|
||||||
if self._mode():
|
|
||||||
self._hvac_list = []
|
|
||||||
self._hvac_mapping = {}
|
|
||||||
self._preset_list = []
|
|
||||||
self._preset_mapping = {}
|
|
||||||
|
|
||||||
if mode_list := self._mode().data_items:
|
|
||||||
for mode in mode_list:
|
|
||||||
ha_mode = HVAC_STATE_MAPPINGS.get(str(mode).lower())
|
|
||||||
ha_preset = PRESET_MAPPINGS.get(str(mode).lower())
|
|
||||||
if mode == AUX_HEAT_ZWAVE_MODE:
|
|
||||||
# Aux Heat should not be included in any mapping
|
|
||||||
self._aux_heat = True
|
|
||||||
elif ha_mode and ha_mode not in self._hvac_mapping:
|
|
||||||
self._hvac_mapping[ha_mode] = mode
|
|
||||||
self._hvac_list.append(ha_mode)
|
|
||||||
elif ha_preset and ha_preset not in self._preset_mapping:
|
|
||||||
self._preset_mapping[ha_preset] = mode
|
|
||||||
self._preset_list.append(ha_preset)
|
|
||||||
else:
|
|
||||||
# If nothing matches
|
|
||||||
self._preset_list.append(mode)
|
|
||||||
|
|
||||||
# Default operation mode
|
|
||||||
for mode in DEFAULT_HVAC_MODES:
|
|
||||||
if mode in self._hvac_mapping:
|
|
||||||
self._default_hvac_mode = mode
|
|
||||||
break
|
|
||||||
|
|
||||||
if self._preset_list:
|
|
||||||
# Presets are supported
|
|
||||||
self._preset_list.append(PRESET_NONE)
|
|
||||||
|
|
||||||
current_mode = self._mode().data
|
|
||||||
_LOGGER.debug("current_mode=%s", current_mode)
|
|
||||||
_hvac_temp = next(
|
|
||||||
(
|
|
||||||
key
|
|
||||||
for key, value in self._hvac_mapping.items()
|
|
||||||
if value == current_mode
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
|
|
||||||
if _hvac_temp is None:
|
|
||||||
# The current mode is not a hvac mode
|
|
||||||
if (
|
|
||||||
"heat" in current_mode.lower()
|
|
||||||
and HVAC_MODE_HEAT in self._hvac_mapping
|
|
||||||
):
|
|
||||||
# The current preset modes maps to HVAC_MODE_HEAT
|
|
||||||
_LOGGER.debug("Mapped to HEAT")
|
|
||||||
self._hvac_mode = HVAC_MODE_HEAT
|
|
||||||
elif (
|
|
||||||
"cool" in current_mode.lower()
|
|
||||||
and HVAC_MODE_COOL in self._hvac_mapping
|
|
||||||
):
|
|
||||||
# The current preset modes maps to HVAC_MODE_COOL
|
|
||||||
_LOGGER.debug("Mapped to COOL")
|
|
||||||
self._hvac_mode = HVAC_MODE_COOL
|
|
||||||
else:
|
|
||||||
# The current preset modes maps to self._default_hvac_mode
|
|
||||||
_LOGGER.debug("Mapped to DEFAULT")
|
|
||||||
self._hvac_mode = self._default_hvac_mode
|
|
||||||
self._preset_mode = next(
|
|
||||||
(
|
|
||||||
key
|
|
||||||
for key, value in self._preset_mapping.items()
|
|
||||||
if value == current_mode
|
|
||||||
),
|
|
||||||
current_mode,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# The current mode is a hvac mode
|
|
||||||
self._hvac_mode = _hvac_temp
|
|
||||||
self._preset_mode = PRESET_NONE
|
|
||||||
|
|
||||||
_LOGGER.debug("self._hvac_mapping=%s", self._hvac_mapping)
|
|
||||||
_LOGGER.debug("self._hvac_list=%s", self._hvac_list)
|
|
||||||
_LOGGER.debug("self._hvac_mode=%s", self._hvac_mode)
|
|
||||||
_LOGGER.debug("self._default_hvac_mode=%s", self._default_hvac_mode)
|
|
||||||
_LOGGER.debug("self._hvac_action=%s", self._hvac_action)
|
|
||||||
_LOGGER.debug("self._aux_heat=%s", self._aux_heat)
|
|
||||||
_LOGGER.debug("self._preset_mapping=%s", self._preset_mapping)
|
|
||||||
_LOGGER.debug("self._preset_list=%s", self._preset_list)
|
|
||||||
_LOGGER.debug("self._preset_mode=%s", self._preset_mode)
|
|
||||||
|
|
||||||
def _update_current_temp(self):
|
|
||||||
"""Update current temperature."""
|
|
||||||
if self.values.temperature:
|
|
||||||
self._current_temperature = self.values.temperature.data
|
|
||||||
device_unit = self.values.temperature.units
|
|
||||||
if device_unit is not None:
|
|
||||||
self._unit = device_unit
|
|
||||||
|
|
||||||
def _update_fan_mode(self):
|
|
||||||
"""Update fan mode."""
|
|
||||||
if self.values.fan_mode:
|
|
||||||
self._current_fan_mode = self.values.fan_mode.data
|
|
||||||
if fan_modes := self.values.fan_mode.data_items:
|
|
||||||
self._fan_modes = list(fan_modes)
|
|
||||||
|
|
||||||
_LOGGER.debug("self._fan_modes=%s", self._fan_modes)
|
|
||||||
_LOGGER.debug("self._current_fan_mode=%s", self._current_fan_mode)
|
|
||||||
|
|
||||||
def _update_swing_mode(self):
|
|
||||||
"""Update swing mode."""
|
|
||||||
if self._zxt_120 == 1:
|
|
||||||
if self.values.zxt_120_swing_mode:
|
|
||||||
self._current_swing_mode = self.values.zxt_120_swing_mode.data
|
|
||||||
swing_modes = self.values.zxt_120_swing_mode.data_items
|
|
||||||
if swing_modes:
|
|
||||||
self._swing_modes = list(swing_modes)
|
|
||||||
_LOGGER.debug("self._swing_modes=%s", self._swing_modes)
|
|
||||||
_LOGGER.debug("self._current_swing_mode=%s", self._current_swing_mode)
|
|
||||||
|
|
||||||
def _update_target_temp(self):
|
|
||||||
"""Update target temperature."""
|
|
||||||
current_setpoints = self._current_mode_setpoints()
|
|
||||||
self._target_temperature = None
|
|
||||||
self._target_temperature_range = (None, None)
|
|
||||||
if len(current_setpoints) == 1:
|
|
||||||
(setpoint,) = current_setpoints
|
|
||||||
if setpoint is not None:
|
|
||||||
self._target_temperature = round((float(setpoint.data)), 1)
|
|
||||||
elif len(current_setpoints) == 2:
|
|
||||||
(setpoint_low, setpoint_high) = current_setpoints
|
|
||||||
target_low, target_high = None, None
|
|
||||||
if setpoint_low is not None:
|
|
||||||
target_low = round((float(setpoint_low.data)), 1)
|
|
||||||
if setpoint_high is not None:
|
|
||||||
target_high = round((float(setpoint_high.data)), 1)
|
|
||||||
self._target_temperature_range = (target_low, target_high)
|
|
||||||
|
|
||||||
def _update_operating_state(self):
|
|
||||||
"""Update operating state."""
|
|
||||||
if self.values.operating_state:
|
|
||||||
mode = self.values.operating_state.data
|
|
||||||
self._hvac_action = HVAC_CURRENT_MAPPINGS.get(str(mode).lower(), mode)
|
|
||||||
|
|
||||||
def _update_fan_state(self):
|
|
||||||
"""Update fan state."""
|
|
||||||
if self.values.fan_action:
|
|
||||||
self._fan_action = self.values.fan_action.data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fan_mode(self):
|
|
||||||
"""Return the fan speed set."""
|
|
||||||
return self._current_fan_mode
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fan_modes(self):
|
|
||||||
"""Return a list of available fan modes."""
|
|
||||||
return self._fan_modes
|
|
||||||
|
|
||||||
@property
|
|
||||||
def swing_mode(self):
|
|
||||||
"""Return the swing mode set."""
|
|
||||||
return self._current_swing_mode
|
|
||||||
|
|
||||||
@property
|
|
||||||
def swing_modes(self):
|
|
||||||
"""Return a list of available swing modes."""
|
|
||||||
return self._swing_modes
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature_unit(self):
|
|
||||||
"""Return the unit of measurement."""
|
|
||||||
if self._unit == "C":
|
|
||||||
return TEMP_CELSIUS
|
|
||||||
if self._unit == "F":
|
|
||||||
return TEMP_FAHRENHEIT
|
|
||||||
return self._unit
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_temperature(self):
|
|
||||||
"""Return the current temperature."""
|
|
||||||
return self._current_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_mode(self):
|
|
||||||
"""Return hvac operation ie. heat, cool mode.
|
|
||||||
|
|
||||||
Need to be one of HVAC_MODE_*.
|
|
||||||
"""
|
|
||||||
if self._mode():
|
|
||||||
return self._hvac_mode
|
|
||||||
return self._default_hvac_mode
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_modes(self):
|
|
||||||
"""Return the list of available hvac operation modes.
|
|
||||||
|
|
||||||
Need to be a subset of HVAC_MODES.
|
|
||||||
"""
|
|
||||||
if self._mode():
|
|
||||||
return self._hvac_list
|
|
||||||
return []
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_action(self):
|
|
||||||
"""Return the current running hvac operation if supported.
|
|
||||||
|
|
||||||
Need to be one of CURRENT_HVAC_*.
|
|
||||||
"""
|
|
||||||
return self._hvac_action
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_aux_heat(self):
|
|
||||||
"""Return true if aux heater."""
|
|
||||||
if not self._aux_heat:
|
|
||||||
return None
|
|
||||||
if self._mode().data == AUX_HEAT_ZWAVE_MODE:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def preset_mode(self):
|
|
||||||
"""Return preset operation ie. eco, away.
|
|
||||||
|
|
||||||
Need to be one of PRESET_*.
|
|
||||||
"""
|
|
||||||
if self._mode():
|
|
||||||
return self._preset_mode
|
|
||||||
return PRESET_NONE
|
|
||||||
|
|
||||||
@property
|
|
||||||
def preset_modes(self):
|
|
||||||
"""Return the list of available preset operation modes.
|
|
||||||
|
|
||||||
Need to be a subset of PRESET_MODES.
|
|
||||||
"""
|
|
||||||
if self._mode():
|
|
||||||
return self._preset_list
|
|
||||||
return []
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self):
|
|
||||||
"""Return the temperature we try to reach."""
|
|
||||||
return self._target_temperature
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature_low(self) -> float | None:
|
|
||||||
"""Return the lowbound target temperature we try to reach."""
|
|
||||||
return self._target_temperature_range[0]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature_high(self) -> float | None:
|
|
||||||
"""Return the highbound target temperature we try to reach."""
|
|
||||||
return self._target_temperature_range[1]
|
|
||||||
|
|
||||||
def set_temperature(self, **kwargs):
|
|
||||||
"""Set new target temperature."""
|
|
||||||
current_setpoints = self._current_mode_setpoints()
|
|
||||||
if len(current_setpoints) == 1:
|
|
||||||
(setpoint,) = current_setpoints
|
|
||||||
target_temp = kwargs.get(ATTR_TEMPERATURE)
|
|
||||||
if setpoint is not None and target_temp is not None:
|
|
||||||
_LOGGER.debug("Set temperature to %s", target_temp)
|
|
||||||
setpoint.data = target_temp
|
|
||||||
elif len(current_setpoints) == 2:
|
|
||||||
(setpoint_low, setpoint_high) = current_setpoints
|
|
||||||
target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
|
|
||||||
target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
|
|
||||||
if setpoint_low is not None and target_temp_low is not None:
|
|
||||||
_LOGGER.debug("Set low temperature to %s", target_temp_low)
|
|
||||||
setpoint_low.data = target_temp_low
|
|
||||||
if setpoint_high is not None and target_temp_high is not None:
|
|
||||||
_LOGGER.debug("Set high temperature to %s", target_temp_high)
|
|
||||||
setpoint_high.data = target_temp_high
|
|
||||||
|
|
||||||
def set_fan_mode(self, fan_mode):
|
|
||||||
"""Set new target fan mode."""
|
|
||||||
_LOGGER.debug("Set fan mode to %s", fan_mode)
|
|
||||||
if not self.values.fan_mode:
|
|
||||||
return
|
|
||||||
self.values.fan_mode.data = fan_mode
|
|
||||||
|
|
||||||
def set_hvac_mode(self, hvac_mode):
|
|
||||||
"""Set new target hvac mode."""
|
|
||||||
_LOGGER.debug("Set hvac_mode to %s", hvac_mode)
|
|
||||||
if not self._mode():
|
|
||||||
return
|
|
||||||
operation_mode = self._hvac_mapping.get(hvac_mode)
|
|
||||||
_LOGGER.debug("Set operation_mode to %s", operation_mode)
|
|
||||||
self._mode().data = operation_mode
|
|
||||||
|
|
||||||
def turn_aux_heat_on(self):
|
|
||||||
"""Turn auxiliary heater on."""
|
|
||||||
if not self._aux_heat:
|
|
||||||
return
|
|
||||||
operation_mode = AUX_HEAT_ZWAVE_MODE
|
|
||||||
_LOGGER.debug("Aux heat on. Set operation mode to %s", operation_mode)
|
|
||||||
self._mode().data = operation_mode
|
|
||||||
|
|
||||||
def turn_aux_heat_off(self):
|
|
||||||
"""Turn auxiliary heater off."""
|
|
||||||
if not self._aux_heat:
|
|
||||||
return
|
|
||||||
if HVAC_MODE_HEAT in self._hvac_mapping:
|
|
||||||
operation_mode = self._hvac_mapping.get(HVAC_MODE_HEAT)
|
|
||||||
else:
|
|
||||||
operation_mode = self._hvac_mapping.get(HVAC_MODE_OFF)
|
|
||||||
_LOGGER.debug("Aux heat off. Set operation mode to %s", operation_mode)
|
|
||||||
self._mode().data = operation_mode
|
|
||||||
|
|
||||||
def set_preset_mode(self, preset_mode):
|
|
||||||
"""Set new target preset mode."""
|
|
||||||
_LOGGER.debug("Set preset_mode to %s", preset_mode)
|
|
||||||
if not self._mode():
|
|
||||||
return
|
|
||||||
if preset_mode == PRESET_NONE:
|
|
||||||
# Activate the current hvac mode
|
|
||||||
self._update_operation_mode()
|
|
||||||
operation_mode = self._hvac_mapping.get(self.hvac_mode)
|
|
||||||
_LOGGER.debug("Set operation_mode to %s", operation_mode)
|
|
||||||
self._mode().data = operation_mode
|
|
||||||
else:
|
|
||||||
operation_mode = self._preset_mapping.get(preset_mode, preset_mode)
|
|
||||||
_LOGGER.debug("Set operation_mode to %s", operation_mode)
|
|
||||||
self._mode().data = operation_mode
|
|
||||||
|
|
||||||
def set_swing_mode(self, swing_mode):
|
|
||||||
"""Set new target swing mode."""
|
|
||||||
_LOGGER.debug("Set swing_mode to %s", swing_mode)
|
|
||||||
if self._zxt_120 == 1 and self.values.zxt_120_swing_mode:
|
|
||||||
self.values.zxt_120_swing_mode.data = swing_mode
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the optional state attributes."""
|
|
||||||
data = super().extra_state_attributes
|
|
||||||
if self._fan_action:
|
|
||||||
data[ATTR_FAN_ACTION] = self._fan_action
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveClimateSingleSetpoint(ZWaveClimateBase):
|
|
||||||
"""Representation of a single setpoint Z-Wave thermostat device."""
|
|
||||||
|
|
||||||
def __init__(self, values, temp_unit):
|
|
||||||
"""Initialize the Z-Wave climate device."""
|
|
||||||
ZWaveClimateBase.__init__(self, values, temp_unit)
|
|
||||||
|
|
||||||
def _mode(self) -> None:
|
|
||||||
"""Return thermostat mode Z-Wave value."""
|
|
||||||
return self.values.mode
|
|
||||||
|
|
||||||
def _current_mode_setpoints(self) -> tuple:
|
|
||||||
"""Return a tuple of current setpoint Z-Wave value(s)."""
|
|
||||||
return (self.values.primary,)
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveClimateMultipleSetpoint(ZWaveClimateBase):
|
|
||||||
"""Representation of a multiple setpoint Z-Wave thermostat device."""
|
|
||||||
|
|
||||||
def __init__(self, values, temp_unit):
|
|
||||||
"""Initialize the Z-Wave climate device."""
|
|
||||||
ZWaveClimateBase.__init__(self, values, temp_unit)
|
|
||||||
|
|
||||||
def _mode(self) -> None:
|
|
||||||
"""Return thermostat mode Z-Wave value."""
|
|
||||||
return self.values.primary
|
|
||||||
|
|
||||||
def _current_mode_setpoints(self) -> tuple:
|
|
||||||
"""Return a tuple of current setpoint Z-Wave value(s)."""
|
|
||||||
current_mode = str(self.values.primary.data).lower()
|
|
||||||
setpoints_names = MODE_SETPOINT_MAPPINGS.get(current_mode, ())
|
|
||||||
return tuple(getattr(self.values, name, None) for name in setpoints_names)
|
|
|
@ -1,95 +0,0 @@
|
||||||
"""Config flow to configure Z-Wave."""
|
|
||||||
# pylint: disable=import-error
|
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
|
|
||||||
from .const import (
|
|
||||||
CONF_NETWORK_KEY,
|
|
||||||
CONF_USB_STICK_PATH,
|
|
||||||
DEFAULT_CONF_USB_STICK_PATH,
|
|
||||||
DOMAIN,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|
||||||
"""Handle a Z-Wave config flow."""
|
|
||||||
|
|
||||||
VERSION = 1
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Initialize the Z-Wave config flow."""
|
|
||||||
self.usb_path = CONF_USB_STICK_PATH
|
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
|
||||||
"""Handle a flow start."""
|
|
||||||
if self._async_current_entries():
|
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
|
||||||
|
|
||||||
errors = {}
|
|
||||||
|
|
||||||
fields = OrderedDict()
|
|
||||||
fields[
|
|
||||||
vol.Required(CONF_USB_STICK_PATH, default=DEFAULT_CONF_USB_STICK_PATH)
|
|
||||||
] = str
|
|
||||||
fields[vol.Optional(CONF_NETWORK_KEY)] = str
|
|
||||||
|
|
||||||
if user_input is not None:
|
|
||||||
# Check if USB path is valid
|
|
||||||
from openzwave.object import ZWaveException
|
|
||||||
from openzwave.option import ZWaveOption
|
|
||||||
|
|
||||||
try:
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
option = await self.hass.async_add_executor_job( # noqa: F841 pylint: disable=unused-variable
|
|
||||||
partial(
|
|
||||||
ZWaveOption,
|
|
||||||
user_input[CONF_USB_STICK_PATH],
|
|
||||||
user_path=self.hass.config.config_dir,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except ZWaveException:
|
|
||||||
errors["base"] = "option_error"
|
|
||||||
return self.async_show_form(
|
|
||||||
step_id="user", data_schema=vol.Schema(fields), errors=errors
|
|
||||||
)
|
|
||||||
|
|
||||||
if user_input.get(CONF_NETWORK_KEY) is None:
|
|
||||||
# Generate a random key
|
|
||||||
from random import choice
|
|
||||||
|
|
||||||
key = ""
|
|
||||||
for i in range(16):
|
|
||||||
key += "0x"
|
|
||||||
key += choice("1234567890ABCDEF")
|
|
||||||
key += choice("1234567890ABCDEF")
|
|
||||||
if i < 15:
|
|
||||||
key += ", "
|
|
||||||
user_input[CONF_NETWORK_KEY] = key
|
|
||||||
|
|
||||||
return self.async_create_entry(
|
|
||||||
title="Z-Wave",
|
|
||||||
data={
|
|
||||||
CONF_USB_STICK_PATH: user_input[CONF_USB_STICK_PATH],
|
|
||||||
CONF_NETWORK_KEY: user_input[CONF_NETWORK_KEY],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.async_show_form(step_id="user", data_schema=vol.Schema(fields))
|
|
||||||
|
|
||||||
async def async_step_import(self, info):
|
|
||||||
"""Import existing configuration from Z-Wave."""
|
|
||||||
if self._async_current_entries():
|
|
||||||
return self.async_abort(reason="already_setup")
|
|
||||||
|
|
||||||
return self.async_create_entry(
|
|
||||||
title="Z-Wave (import from configuration.yaml)",
|
|
||||||
data={
|
|
||||||
CONF_USB_STICK_PATH: info.get(CONF_USB_STICK_PATH),
|
|
||||||
CONF_NETWORK_KEY: info.get(CONF_NETWORK_KEY),
|
|
||||||
},
|
|
||||||
)
|
|
|
@ -1,395 +0,0 @@
|
||||||
"""Z-Wave Constants."""
|
|
||||||
DOMAIN = "zwave"
|
|
||||||
|
|
||||||
ATTR_NODE_ID = "node_id"
|
|
||||||
ATTR_TARGET_NODE_ID = "target_node_id"
|
|
||||||
ATTR_ASSOCIATION = "association"
|
|
||||||
ATTR_INSTANCE = "instance"
|
|
||||||
ATTR_GROUP = "group"
|
|
||||||
ATTR_VALUE_ID = "value_id"
|
|
||||||
ATTR_MESSAGES = "messages"
|
|
||||||
ATTR_RETURN_ROUTES = "return_routes"
|
|
||||||
ATTR_SCENE_ID = "scene_id"
|
|
||||||
ATTR_SCENE_DATA = "scene_data"
|
|
||||||
ATTR_BASIC_LEVEL = "basic_level"
|
|
||||||
ATTR_CONFIG_PARAMETER = "parameter"
|
|
||||||
ATTR_CONFIG_SIZE = "size"
|
|
||||||
ATTR_CONFIG_VALUE = "value"
|
|
||||||
ATTR_POLL_INTENSITY = "poll_intensity"
|
|
||||||
ATTR_VALUE_INDEX = "value_index"
|
|
||||||
ATTR_VALUE_INSTANCE = "value_instance"
|
|
||||||
ATTR_UPDATE_IDS = "update_ids"
|
|
||||||
NETWORK_READY_WAIT_SECS = 300
|
|
||||||
NODE_READY_WAIT_SECS = 30
|
|
||||||
|
|
||||||
CONF_AUTOHEAL = "autoheal"
|
|
||||||
CONF_DEBUG = "debug"
|
|
||||||
CONF_POLLING_INTERVAL = "polling_interval"
|
|
||||||
CONF_USB_STICK_PATH = "usb_path"
|
|
||||||
CONF_CONFIG_PATH = "config_path"
|
|
||||||
CONF_NETWORK_KEY = "network_key"
|
|
||||||
|
|
||||||
DEFAULT_CONF_AUTOHEAL = False
|
|
||||||
DEFAULT_CONF_USB_STICK_PATH = "/zwaveusbstick"
|
|
||||||
DEFAULT_POLLING_INTERVAL = 60000
|
|
||||||
DEFAULT_DEBUG = False
|
|
||||||
|
|
||||||
DISCOVERY_DEVICE = "device"
|
|
||||||
|
|
||||||
DATA_DEVICES = "zwave_devices"
|
|
||||||
DATA_NETWORK = "zwave_network"
|
|
||||||
DATA_ENTITY_VALUES = "zwave_entity_values"
|
|
||||||
DATA_ZWAVE_CONFIG = "zwave_config"
|
|
||||||
|
|
||||||
SERVICE_CHANGE_ASSOCIATION = "change_association"
|
|
||||||
SERVICE_ADD_NODE = "add_node"
|
|
||||||
SERVICE_ADD_NODE_SECURE = "add_node_secure"
|
|
||||||
SERVICE_REMOVE_NODE = "remove_node"
|
|
||||||
SERVICE_CANCEL_COMMAND = "cancel_command"
|
|
||||||
SERVICE_HEAL_NETWORK = "heal_network"
|
|
||||||
SERVICE_HEAL_NODE = "heal_node"
|
|
||||||
SERVICE_SOFT_RESET = "soft_reset"
|
|
||||||
SERVICE_TEST_NODE = "test_node"
|
|
||||||
SERVICE_TEST_NETWORK = "test_network"
|
|
||||||
SERVICE_SET_CONFIG_PARAMETER = "set_config_parameter"
|
|
||||||
SERVICE_SET_NODE_VALUE = "set_node_value"
|
|
||||||
SERVICE_REFRESH_NODE_VALUE = "refresh_node_value"
|
|
||||||
SERVICE_PRINT_CONFIG_PARAMETER = "print_config_parameter"
|
|
||||||
SERVICE_PRINT_NODE = "print_node"
|
|
||||||
SERVICE_REMOVE_FAILED_NODE = "remove_failed_node"
|
|
||||||
SERVICE_REPLACE_FAILED_NODE = "replace_failed_node"
|
|
||||||
SERVICE_SET_POLL_INTENSITY = "set_poll_intensity"
|
|
||||||
SERVICE_SET_WAKEUP = "set_wakeup"
|
|
||||||
SERVICE_STOP_NETWORK = "stop_network"
|
|
||||||
SERVICE_START_NETWORK = "start_network"
|
|
||||||
SERVICE_RENAME_NODE = "rename_node"
|
|
||||||
SERVICE_RENAME_VALUE = "rename_value"
|
|
||||||
SERVICE_REFRESH_ENTITY = "refresh_entity"
|
|
||||||
SERVICE_REFRESH_NODE = "refresh_node"
|
|
||||||
SERVICE_RESET_NODE_METERS = "reset_node_meters"
|
|
||||||
|
|
||||||
EVENT_SCENE_ACTIVATED = "zwave.scene_activated"
|
|
||||||
EVENT_NODE_EVENT = "zwave.node_event"
|
|
||||||
EVENT_NETWORK_READY = "zwave.network_ready"
|
|
||||||
EVENT_NETWORK_COMPLETE = "zwave.network_complete"
|
|
||||||
EVENT_NETWORK_COMPLETE_SOME_DEAD = "zwave.network_complete_some_dead"
|
|
||||||
EVENT_NETWORK_START = "zwave.network_start"
|
|
||||||
EVENT_NETWORK_STOP = "zwave.network_stop"
|
|
||||||
|
|
||||||
COMMAND_CLASS_ALARM = 113
|
|
||||||
COMMAND_CLASS_ANTITHEFT = 93
|
|
||||||
COMMAND_CLASS_APPLICATION_CAPABILITY = 87
|
|
||||||
COMMAND_CLASS_APPLICATION_STATUS = 34
|
|
||||||
COMMAND_CLASS_ASSOCIATION = 133
|
|
||||||
COMMAND_CLASS_ASSOCIATION_COMMAND_CONFIGURATION = 155
|
|
||||||
COMMAND_CLASS_ASSOCIATION_GRP_INFO = 89
|
|
||||||
COMMAND_CLASS_BARRIER_OPERATOR = 102
|
|
||||||
COMMAND_CLASS_BASIC = 32
|
|
||||||
COMMAND_CLASS_BASIC_TARIFF_INFO = 54
|
|
||||||
COMMAND_CLASS_BASIC_WINDOW_COVERING = 80
|
|
||||||
COMMAND_CLASS_BATTERY = 128
|
|
||||||
COMMAND_CLASS_CENTRAL_SCENE = 91
|
|
||||||
COMMAND_CLASS_CLIMATE_CONTROL_SCHEDULE = 70
|
|
||||||
COMMAND_CLASS_CLOCK = 129
|
|
||||||
COMMAND_CLASS_CONFIGURATION = 112
|
|
||||||
COMMAND_CLASS_CONTROLLER_REPLICATION = 33
|
|
||||||
COMMAND_CLASS_CRC_16_ENCAP = 86
|
|
||||||
COMMAND_CLASS_DCP_CONFIG = 58
|
|
||||||
COMMAND_CLASS_DCP_MONITOR = 59
|
|
||||||
COMMAND_CLASS_DEVICE_RESET_LOCALLY = 90
|
|
||||||
COMMAND_CLASS_DOOR_LOCK = 98
|
|
||||||
COMMAND_CLASS_DOOR_LOCK_LOGGING = 76
|
|
||||||
COMMAND_CLASS_ENERGY_PRODUCTION = 144
|
|
||||||
COMMAND_CLASS_ENTRY_CONTROL = 111
|
|
||||||
COMMAND_CLASS_FIRMWARE_UPDATE_MD = 122
|
|
||||||
COMMAND_CLASS_GEOGRAPHIC_LOCATION = 140
|
|
||||||
COMMAND_CLASS_GROUPING_NAME = 123
|
|
||||||
COMMAND_CLASS_HAIL = 130
|
|
||||||
COMMAND_CLASS_HRV_CONTROL = 57
|
|
||||||
COMMAND_CLASS_HRV_STATUS = 55
|
|
||||||
COMMAND_CLASS_HUMIDITY_CONTROL_MODE = 109
|
|
||||||
COMMAND_CLASS_HUMIDITY_CONTROL_OPERATING_STATE = 110
|
|
||||||
COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT = 100
|
|
||||||
COMMAND_CLASS_INDICATOR = 135
|
|
||||||
COMMAND_CLASS_IP_ASSOCIATION = 92
|
|
||||||
COMMAND_CLASS_IP_CONFIGURATION = 14
|
|
||||||
COMMAND_CLASS_IRRIGATION = 107
|
|
||||||
COMMAND_CLASS_LANGUAGE = 137
|
|
||||||
COMMAND_CLASS_LOCK = 118
|
|
||||||
COMMAND_CLASS_MAILBOX = 105
|
|
||||||
COMMAND_CLASS_MANUFACTURER_PROPRIETARY = 145
|
|
||||||
COMMAND_CLASS_MANUFACTURER_SPECIFIC = 114
|
|
||||||
COMMAND_CLASS_MARK = 239
|
|
||||||
COMMAND_CLASS_METER = 50
|
|
||||||
COMMAND_CLASS_METER_PULSE = 53
|
|
||||||
COMMAND_CLASS_METER_TBL_CONFIG = 60
|
|
||||||
COMMAND_CLASS_METER_TBL_MONITOR = 61
|
|
||||||
COMMAND_CLASS_METER_TBL_PUSH = 62
|
|
||||||
COMMAND_CLASS_MTP_WINDOW_COVERING = 81
|
|
||||||
COMMAND_CLASS_MULTI_CHANNEL = 96
|
|
||||||
COMMAND_CLASS_MULTI_CHANNEL_ASSOCIATION = 142
|
|
||||||
COMMAND_CLASS_MULTI_COMMAND = 143
|
|
||||||
COMMAND_CLASS_NETWORK_MANAGEMENT_BASIC = 77
|
|
||||||
COMMAND_CLASS_NETWORK_MANAGEMENT_INCLUSION = 52
|
|
||||||
COMMAND_CLASS_NETWORK_MANAGEMENT_PRIMARY = 84
|
|
||||||
COMMAND_CLASS_NETWORK_MANAGEMENT_PROXY = 82
|
|
||||||
COMMAND_CLASS_NO_OPERATION = 0
|
|
||||||
COMMAND_CLASS_NODE_NAMING = 119
|
|
||||||
COMMAND_CLASS_NON_INTEROPERABLE = 240
|
|
||||||
COMMAND_CLASS_NOTIFICATION = 113
|
|
||||||
COMMAND_CLASS_POWERLEVEL = 115
|
|
||||||
COMMAND_CLASS_PREPAYMENT = 63
|
|
||||||
COMMAND_CLASS_PREPAYMENT_ENCAPSULATION = 65
|
|
||||||
COMMAND_CLASS_PROPRIETARY = 136
|
|
||||||
COMMAND_CLASS_PROTECTION = 117
|
|
||||||
COMMAND_CLASS_RATE_TBL_CONFIG = 72
|
|
||||||
COMMAND_CLASS_RATE_TBL_MONITOR = 73
|
|
||||||
COMMAND_CLASS_REMOTE_ASSOCIATION_ACTIVATE = 124
|
|
||||||
COMMAND_CLASS_REMOTE_ASSOCIATION = 125
|
|
||||||
COMMAND_CLASS_SCENE_ACTIVATION = 43
|
|
||||||
COMMAND_CLASS_SCENE_ACTUATOR_CONF = 44
|
|
||||||
COMMAND_CLASS_SCENE_CONTROLLER_CONF = 45
|
|
||||||
COMMAND_CLASS_SCHEDULE = 83
|
|
||||||
COMMAND_CLASS_SCHEDULE_ENTRY_LOCK = 78
|
|
||||||
COMMAND_CLASS_SCREEN_ATTRIBUTES = 147
|
|
||||||
COMMAND_CLASS_SCREEN_MD = 146
|
|
||||||
COMMAND_CLASS_SECURITY = 152
|
|
||||||
COMMAND_CLASS_SECURITY_SCHEME0_MARK = 61696
|
|
||||||
COMMAND_CLASS_SENSOR_ALARM = 156
|
|
||||||
COMMAND_CLASS_SENSOR_BINARY = 48
|
|
||||||
COMMAND_CLASS_SENSOR_CONFIGURATION = 158
|
|
||||||
COMMAND_CLASS_SENSOR_MULTILEVEL = 49
|
|
||||||
COMMAND_CLASS_SILENCE_ALARM = 157
|
|
||||||
COMMAND_CLASS_SIMPLE_AV_CONTROL = 148
|
|
||||||
COMMAND_CLASS_SUPERVISION = 108
|
|
||||||
COMMAND_CLASS_SWITCH_ALL = 39
|
|
||||||
COMMAND_CLASS_SWITCH_BINARY = 37
|
|
||||||
COMMAND_CLASS_SWITCH_COLOR = 51
|
|
||||||
COMMAND_CLASS_SWITCH_MULTILEVEL = 38
|
|
||||||
COMMAND_CLASS_SWITCH_TOGGLE_BINARY = 40
|
|
||||||
COMMAND_CLASS_SWITCH_TOGGLE_MULTILEVEL = 41
|
|
||||||
COMMAND_CLASS_TARIFF_TBL_CONFIG = 74
|
|
||||||
COMMAND_CLASS_TARIFF_TBL_MONITOR = 75
|
|
||||||
COMMAND_CLASS_THERMOSTAT_FAN_MODE = 68
|
|
||||||
COMMAND_CLASS_THERMOSTAT_FAN_ACTION = 69
|
|
||||||
COMMAND_CLASS_THERMOSTAT_MODE = 64
|
|
||||||
COMMAND_CLASS_THERMOSTAT_OPERATING_STATE = 66
|
|
||||||
COMMAND_CLASS_THERMOSTAT_SETBACK = 71
|
|
||||||
COMMAND_CLASS_THERMOSTAT_SETPOINT = 67
|
|
||||||
COMMAND_CLASS_TIME = 138
|
|
||||||
COMMAND_CLASS_TIME_PARAMETERS = 139
|
|
||||||
COMMAND_CLASS_TRANSPORT_SERVICE = 85
|
|
||||||
COMMAND_CLASS_USER_CODE = 99
|
|
||||||
COMMAND_CLASS_VERSION = 134
|
|
||||||
COMMAND_CLASS_WAKE_UP = 132
|
|
||||||
COMMAND_CLASS_ZIP = 35
|
|
||||||
COMMAND_CLASS_ZIP_NAMING = 104
|
|
||||||
COMMAND_CLASS_ZIP_ND = 88
|
|
||||||
COMMAND_CLASS_ZIP_6LOWPAN = 79
|
|
||||||
COMMAND_CLASS_ZIP_GATEWAY = 95
|
|
||||||
COMMAND_CLASS_ZIP_PORTAL = 97
|
|
||||||
COMMAND_CLASS_ZWAVEPLUS_INFO = 94
|
|
||||||
COMMAND_CLASS_WHATEVER = None # Match ALL
|
|
||||||
COMMAND_CLASS_WINDOW_COVERING = 106
|
|
||||||
|
|
||||||
GENERIC_TYPE_WHATEVER = None # Match ALL
|
|
||||||
SPECIFIC_TYPE_WHATEVER = None # Match ALL
|
|
||||||
SPECIFIC_TYPE_NOT_USED = 0 # Available in all Generic types
|
|
||||||
|
|
||||||
GENERIC_TYPE_AV_CONTROL_POINT = 3
|
|
||||||
SPECIFIC_TYPE_DOORBELL = 18
|
|
||||||
SPECIFIC_TYPE_SATELLITE_RECEIVER = 4
|
|
||||||
SPECIFIC_TYPE_SATELLITE_RECEIVER_V2 = 17
|
|
||||||
|
|
||||||
GENERIC_TYPE_DISPLAY = 4
|
|
||||||
SPECIFIC_TYPE_SIMPLE_DISPLAY = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_ENTRY_CONTROL = 64
|
|
||||||
SPECIFIC_TYPE_DOOR_LOCK = 1
|
|
||||||
SPECIFIC_TYPE_ADVANCED_DOOR_LOCK = 2
|
|
||||||
SPECIFIC_TYPE_SECURE_KEYPAD_DOOR_LOCK = 3
|
|
||||||
SPECIFIC_TYPE_SECURE_KEYPAD_DOOR_LOCK_DEADBOLT = 4
|
|
||||||
SPECIFIC_TYPE_SECURE_DOOR = 5
|
|
||||||
SPECIFIC_TYPE_SECURE_GATE = 6
|
|
||||||
SPECIFIC_TYPE_SECURE_BARRIER_ADDON = 7
|
|
||||||
SPECIFIC_TYPE_SECURE_BARRIER_OPEN_ONLY = 8
|
|
||||||
SPECIFIC_TYPE_SECURE_BARRIER_CLOSE_ONLY = 9
|
|
||||||
SPECIFIC_TYPE_SECURE_LOCKBOX = 10
|
|
||||||
SPECIFIC_TYPE_SECURE_KEYPAD = 11
|
|
||||||
|
|
||||||
GENERIC_TYPE_GENERIC_CONTROLLER = 1
|
|
||||||
SPECIFIC_TYPE_PORTABLE_CONTROLLER = 1
|
|
||||||
SPECIFIC_TYPE_PORTABLE_SCENE_CONTROLLER = 2
|
|
||||||
SPECIFIC_TYPE_PORTABLE_INSTALLER_TOOL = 3
|
|
||||||
SPECIFIC_TYPE_REMOTE_CONTROL_AV = 4
|
|
||||||
SPECIFIC_TYPE_REMOTE_CONTROL_SIMPLE = 6
|
|
||||||
|
|
||||||
GENERIC_TYPE_METER = 49
|
|
||||||
SPECIFIC_TYPE_SIMPLE_METER = 1
|
|
||||||
SPECIFIC_TYPE_ADV_ENERGY_CONTROL = 2
|
|
||||||
SPECIFIC_TYPE_WHOLE_HOME_METER_SIMPLE = 3
|
|
||||||
|
|
||||||
GENERIC_TYPE_METER_PULSE = 48
|
|
||||||
|
|
||||||
GENERIC_TYPE_NON_INTEROPERABLE = 255
|
|
||||||
|
|
||||||
GENERIC_TYPE_REPEATER_SLAVE = 15
|
|
||||||
SPECIFIC_TYPE_REPEATER_SLAVE = 1
|
|
||||||
SPECIFIC_TYPE_VIRTUAL_NODE = 2
|
|
||||||
|
|
||||||
GENERIC_TYPE_SECURITY_PANEL = 23
|
|
||||||
SPECIFIC_TYPE_ZONED_SECURITY_PANEL = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_SEMI_INTEROPERABLE = 80
|
|
||||||
SPECIFIC_TYPE_ENERGY_PRODUCTION = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_SENSOR_ALARM = 161
|
|
||||||
SPECIFIC_TYPE_ADV_ZENSOR_NET_ALARM_SENSOR = 5
|
|
||||||
SPECIFIC_TYPE_ADV_ZENSOR_NET_SMOKE_SENSOR = 10
|
|
||||||
SPECIFIC_TYPE_BASIC_ROUTING_ALARM_SENSOR = 1
|
|
||||||
SPECIFIC_TYPE_BASIC_ROUTING_SMOKE_SENSOR = 6
|
|
||||||
SPECIFIC_TYPE_BASIC_ZENSOR_NET_ALARM_SENSOR = 3
|
|
||||||
SPECIFIC_TYPE_BASIC_ZENSOR_NET_SMOKE_SENSOR = 8
|
|
||||||
SPECIFIC_TYPE_ROUTING_ALARM_SENSOR = 2
|
|
||||||
SPECIFIC_TYPE_ROUTING_SMOKE_SENSOR = 7
|
|
||||||
SPECIFIC_TYPE_ZENSOR_NET_ALARM_SENSOR = 4
|
|
||||||
SPECIFIC_TYPE_ZENSOR_NET_SMOKE_SENSOR = 9
|
|
||||||
SPECIFIC_TYPE_ALARM_SENSOR = 11
|
|
||||||
|
|
||||||
GENERIC_TYPE_SENSOR_BINARY = 32
|
|
||||||
SPECIFIC_TYPE_ROUTING_SENSOR_BINARY = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_SENSOR_MULTILEVEL = 33
|
|
||||||
SPECIFIC_TYPE_ROUTING_SENSOR_MULTILEVEL = 1
|
|
||||||
SPECIFIC_TYPE_CHIMNEY_FAN = 2
|
|
||||||
|
|
||||||
GENERIC_TYPE_STATIC_CONTROLLER = 2
|
|
||||||
SPECIFIC_TYPE_PC_CONTROLLER = 1
|
|
||||||
SPECIFIC_TYPE_SCENE_CONTROLLER = 2
|
|
||||||
SPECIFIC_TYPE_STATIC_INSTALLER_TOOL = 3
|
|
||||||
SPECIFIC_TYPE_SET_TOP_BOX = 4
|
|
||||||
SPECIFIC_TYPE_SUB_SYSTEM_CONTROLLER = 5
|
|
||||||
SPECIFIC_TYPE_TV = 6
|
|
||||||
SPECIFIC_TYPE_GATEWAY = 7
|
|
||||||
|
|
||||||
GENERIC_TYPE_SWITCH_BINARY = 16
|
|
||||||
SPECIFIC_TYPE_POWER_SWITCH_BINARY = 1
|
|
||||||
SPECIFIC_TYPE_SCENE_SWITCH_BINARY = 3
|
|
||||||
SPECIFIC_TYPE_POWER_STRIP = 4
|
|
||||||
SPECIFIC_TYPE_SIREN = 5
|
|
||||||
SPECIFIC_TYPE_VALVE_OPEN_CLOSE = 6
|
|
||||||
SPECIFIC_TYPE_COLOR_TUNABLE_BINARY = 2
|
|
||||||
SPECIFIC_TYPE_IRRIGATION_CONTROLLER = 7
|
|
||||||
|
|
||||||
GENERIC_TYPE_SWITCH_MULTILEVEL = 17
|
|
||||||
SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL = 5
|
|
||||||
SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL = 6
|
|
||||||
SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL = 7
|
|
||||||
SPECIFIC_TYPE_MOTOR_MULTIPOSITION = 3
|
|
||||||
SPECIFIC_TYPE_POWER_SWITCH_MULTILEVEL = 1
|
|
||||||
SPECIFIC_TYPE_SCENE_SWITCH_MULTILEVEL = 4
|
|
||||||
SPECIFIC_TYPE_FAN_SWITCH = 8
|
|
||||||
SPECIFIC_TYPE_COLOR_TUNABLE_MULTILEVEL = 2
|
|
||||||
|
|
||||||
GENERIC_TYPE_SWITCH_REMOTE = 18
|
|
||||||
SPECIFIC_TYPE_REMOTE_BINARY = 1
|
|
||||||
SPECIFIC_TYPE_REMOTE_MULTILEVEL = 2
|
|
||||||
SPECIFIC_TYPE_REMOTE_TOGGLE_BINARY = 3
|
|
||||||
SPECIFIC_TYPE_REMOTE_TOGGLE_MULTILEVEL = 4
|
|
||||||
|
|
||||||
GENERIC_TYPE_SWITCH_TOGGLE = 19
|
|
||||||
SPECIFIC_TYPE_SWITCH_TOGGLE_BINARY = 1
|
|
||||||
SPECIFIC_TYPE_SWITCH_TOGGLE_MULTILEVEL = 2
|
|
||||||
|
|
||||||
GENERIC_TYPE_THERMOSTAT = 8
|
|
||||||
SPECIFIC_TYPE_SETBACK_SCHEDULE_THERMOSTAT = 3
|
|
||||||
SPECIFIC_TYPE_SETBACK_THERMOSTAT = 5
|
|
||||||
SPECIFIC_TYPE_SETPOINT_THERMOSTAT = 4
|
|
||||||
SPECIFIC_TYPE_THERMOSTAT_GENERAL = 2
|
|
||||||
SPECIFIC_TYPE_THERMOSTAT_GENERAL_V2 = 6
|
|
||||||
SPECIFIC_TYPE_THERMOSTAT_HEATING = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_VENTILATION = 22
|
|
||||||
SPECIFIC_TYPE_RESIDENTIAL_HRV = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_WINDOWS_COVERING = 9
|
|
||||||
SPECIFIC_TYPE_SIMPLE_WINDOW_COVERING = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_ZIP_NODE = 21
|
|
||||||
SPECIFIC_TYPE_ZIP_ADV_NODE = 2
|
|
||||||
SPECIFIC_TYPE_ZIP_TUN_NODE = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_WALL_CONTROLLER = 24
|
|
||||||
SPECIFIC_TYPE_BASIC_WALL_CONTROLLER = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_NETWORK_EXTENDER = 5
|
|
||||||
SPECIFIC_TYPE_SECURE_EXTENDER = 1
|
|
||||||
|
|
||||||
GENERIC_TYPE_APPLIANCE = 6
|
|
||||||
SPECIFIC_TYPE_GENERAL_APPLIANCE = 1
|
|
||||||
SPECIFIC_TYPE_KITCHEN_APPLIANCE = 2
|
|
||||||
SPECIFIC_TYPE_LAUNDRY_APPLIANCE = 3
|
|
||||||
|
|
||||||
GENERIC_TYPE_SENSOR_NOTIFICATION = 7
|
|
||||||
SPECIFIC_TYPE_NOTIFICATION_SENSOR = 1
|
|
||||||
|
|
||||||
GENRE_WHATEVER = None
|
|
||||||
GENRE_USER = "User"
|
|
||||||
GENRE_SYSTEM = "System"
|
|
||||||
|
|
||||||
TYPE_WHATEVER = None
|
|
||||||
TYPE_BYTE = "Byte"
|
|
||||||
TYPE_BOOL = "Bool"
|
|
||||||
TYPE_DECIMAL = "Decimal"
|
|
||||||
TYPE_INT = "Int"
|
|
||||||
TYPE_LIST = "List"
|
|
||||||
TYPE_STRING = "String"
|
|
||||||
TYPE_BUTTON = "Button"
|
|
||||||
|
|
||||||
DISC_COMMAND_CLASS = "command_class"
|
|
||||||
DISC_COMPONENT = "component"
|
|
||||||
DISC_GENERIC_DEVICE_CLASS = "generic_device_class"
|
|
||||||
DISC_GENRE = "genre"
|
|
||||||
DISC_INDEX = "index"
|
|
||||||
DISC_INSTANCE = "instance"
|
|
||||||
DISC_NODE_ID = "node_id"
|
|
||||||
DISC_OPTIONAL = "optional"
|
|
||||||
DISC_PRIMARY = "primary"
|
|
||||||
DISC_SCHEMAS = "schemas"
|
|
||||||
DISC_SPECIFIC_DEVICE_CLASS = "specific_device_class"
|
|
||||||
DISC_TYPE = "type"
|
|
||||||
DISC_VALUES = "values"
|
|
||||||
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/Alarm.cpp#L49
|
|
||||||
# See also:
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/Alarm.cpp#L275
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/Alarm.cpp#L278
|
|
||||||
INDEX_ALARM_TYPE = 0
|
|
||||||
INDEX_ALARM_LEVEL = 1
|
|
||||||
INDEX_ALARM_ACCESS_CONTROL = 9
|
|
||||||
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/de1c0e60edf1d1bee81f1ae54b1f58e66c6fd8ed/cpp/src/command_classes/BarrierOperator.cpp#L69
|
|
||||||
INDEX_BARRIER_OPERATOR_LABEL = 1
|
|
||||||
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/DoorLock.cpp#L77
|
|
||||||
INDEX_DOOR_LOCK_LOCK = 0
|
|
||||||
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/Meter.cpp#L114
|
|
||||||
# See also:
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/Meter.cpp#L279
|
|
||||||
INDEX_METER_POWER = 8
|
|
||||||
INDEX_METER_RESET = 33
|
|
||||||
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/SensorMultilevel.cpp#L50
|
|
||||||
INDEX_SENSOR_MULTILEVEL_TEMPERATURE = 1
|
|
||||||
INDEX_SENSOR_MULTILEVEL_POWER = 4
|
|
||||||
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/Color.cpp#L109
|
|
||||||
INDEX_SWITCH_COLOR_COLOR = 0
|
|
||||||
INDEX_SWITCH_COLOR_CHANNELS = 2
|
|
||||||
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/67f180eb565f0054f517ff395c71ecd706f6a837/cpp/src/command_classes/SwitchMultilevel.cpp#L54
|
|
||||||
INDEX_SWITCH_MULTILEVEL_LEVEL = 0
|
|
||||||
INDEX_SWITCH_MULTILEVEL_BRIGHT = 1
|
|
||||||
INDEX_SWITCH_MULTILEVEL_DIM = 2
|
|
||||||
INDEX_SWITCH_MULTILEVEL_DURATION = 5
|
|
|
@ -1,216 +0,0 @@
|
||||||
"""Support for Z-Wave covers."""
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
|
||||||
ATTR_POSITION,
|
|
||||||
DOMAIN,
|
|
||||||
SUPPORT_CLOSE,
|
|
||||||
SUPPORT_OPEN,
|
|
||||||
CoverDeviceClass,
|
|
||||||
CoverEntity,
|
|
||||||
)
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
|
|
||||||
from . import (
|
|
||||||
CONF_INVERT_OPENCLOSE_BUTTONS,
|
|
||||||
CONF_INVERT_PERCENT,
|
|
||||||
ZWaveDeviceEntity,
|
|
||||||
workaround,
|
|
||||||
)
|
|
||||||
from .const import (
|
|
||||||
COMMAND_CLASS_BARRIER_OPERATOR,
|
|
||||||
COMMAND_CLASS_SWITCH_BINARY,
|
|
||||||
COMMAND_CLASS_SWITCH_MULTILEVEL,
|
|
||||||
DATA_NETWORK,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
SUPPORT_GARAGE = SUPPORT_OPEN | SUPPORT_CLOSE
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave Cover from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_cover(cover):
|
|
||||||
"""Add Z-Wave Cover."""
|
|
||||||
async_add_entities([cover])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_cover", async_add_cover)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(hass, values, node_config, **kwargs):
|
|
||||||
"""Create Z-Wave entity device."""
|
|
||||||
invert_buttons = node_config.get(CONF_INVERT_OPENCLOSE_BUTTONS)
|
|
||||||
invert_percent = node_config.get(CONF_INVERT_PERCENT)
|
|
||||||
if (
|
|
||||||
values.primary.command_class == COMMAND_CLASS_SWITCH_MULTILEVEL
|
|
||||||
and values.primary.index == 0
|
|
||||||
):
|
|
||||||
return ZwaveRollershutter(hass, values, invert_buttons, invert_percent)
|
|
||||||
if values.primary.command_class == COMMAND_CLASS_SWITCH_BINARY:
|
|
||||||
return ZwaveGarageDoorSwitch(values)
|
|
||||||
if values.primary.command_class == COMMAND_CLASS_BARRIER_OPERATOR:
|
|
||||||
return ZwaveGarageDoorBarrier(values)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveRollershutter(ZWaveDeviceEntity, CoverEntity):
|
|
||||||
"""Representation of an Z-Wave cover."""
|
|
||||||
|
|
||||||
def __init__(self, hass, values, invert_buttons, invert_percent):
|
|
||||||
"""Initialize the Z-Wave rollershutter."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self._network = hass.data[DATA_NETWORK]
|
|
||||||
self._open_id = None
|
|
||||||
self._close_id = None
|
|
||||||
self._current_position = None
|
|
||||||
self._invert_buttons = invert_buttons
|
|
||||||
self._invert_percent = invert_percent
|
|
||||||
|
|
||||||
self._workaround = workaround.get_device_mapping(values.primary)
|
|
||||||
if self._workaround:
|
|
||||||
_LOGGER.debug("Using workaround %s", self._workaround)
|
|
||||||
self.update_properties()
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle data changes for node values."""
|
|
||||||
# Position value
|
|
||||||
self._current_position = self.values.primary.data
|
|
||||||
|
|
||||||
if (
|
|
||||||
self.values.open
|
|
||||||
and self.values.close
|
|
||||||
and self._open_id is None
|
|
||||||
and self._close_id is None
|
|
||||||
):
|
|
||||||
if self._invert_buttons:
|
|
||||||
self._open_id = self.values.close.value_id
|
|
||||||
self._close_id = self.values.open.value_id
|
|
||||||
else:
|
|
||||||
self._open_id = self.values.open.value_id
|
|
||||||
self._close_id = self.values.close.value_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_closed(self):
|
|
||||||
"""Return if the cover is closed."""
|
|
||||||
if self.current_cover_position is None:
|
|
||||||
return None
|
|
||||||
if self.current_cover_position > 0:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_cover_position(self):
|
|
||||||
"""Return the current position of Zwave roller shutter."""
|
|
||||||
if self._workaround == workaround.WORKAROUND_NO_POSITION:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self._current_position is not None:
|
|
||||||
if self._current_position <= 5:
|
|
||||||
return 100 if self._invert_percent else 0
|
|
||||||
if self._current_position >= 95:
|
|
||||||
return 0 if self._invert_percent else 100
|
|
||||||
return (
|
|
||||||
100 - self._current_position
|
|
||||||
if self._invert_percent
|
|
||||||
else self._current_position
|
|
||||||
)
|
|
||||||
|
|
||||||
def open_cover(self, **kwargs):
|
|
||||||
"""Move the roller shutter up."""
|
|
||||||
self._network.manager.pressButton(self._open_id)
|
|
||||||
|
|
||||||
def close_cover(self, **kwargs):
|
|
||||||
"""Move the roller shutter down."""
|
|
||||||
self._network.manager.pressButton(self._close_id)
|
|
||||||
|
|
||||||
def set_cover_position(self, **kwargs):
|
|
||||||
"""Move the roller shutter to a specific position."""
|
|
||||||
self.node.set_dimmer(
|
|
||||||
self.values.primary.value_id,
|
|
||||||
(100 - kwargs.get(ATTR_POSITION))
|
|
||||||
if self._invert_percent
|
|
||||||
else kwargs.get(ATTR_POSITION),
|
|
||||||
)
|
|
||||||
|
|
||||||
def stop_cover(self, **kwargs):
|
|
||||||
"""Stop the roller shutter."""
|
|
||||||
self._network.manager.releaseButton(self._open_id)
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveGarageDoorBase(ZWaveDeviceEntity, CoverEntity):
|
|
||||||
"""Base class for a Zwave garage door device."""
|
|
||||||
|
|
||||||
def __init__(self, values):
|
|
||||||
"""Initialize the zwave garage door."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self._state = None
|
|
||||||
self.update_properties()
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle data changes for node values."""
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
_LOGGER.debug("self._state=%s", self._state)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this device, from CoverDeviceClass."""
|
|
||||||
return CoverDeviceClass.GARAGE
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Flag supported features."""
|
|
||||||
return SUPPORT_GARAGE
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveGarageDoorSwitch(ZwaveGarageDoorBase):
|
|
||||||
"""Representation of a switch based Zwave garage door device."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_closed(self):
|
|
||||||
"""Return the current position of Zwave garage door."""
|
|
||||||
return not self._state
|
|
||||||
|
|
||||||
def close_cover(self, **kwargs):
|
|
||||||
"""Close the garage door."""
|
|
||||||
self.values.primary.data = False
|
|
||||||
|
|
||||||
def open_cover(self, **kwargs):
|
|
||||||
"""Open the garage door."""
|
|
||||||
self.values.primary.data = True
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveGarageDoorBarrier(ZwaveGarageDoorBase):
|
|
||||||
"""Representation of a barrier operator Zwave garage door device."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_opening(self):
|
|
||||||
"""Return true if cover is in an opening state."""
|
|
||||||
return self._state == "Opening"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_closing(self):
|
|
||||||
"""Return true if cover is in a closing state."""
|
|
||||||
return self._state == "Closing"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_closed(self):
|
|
||||||
"""Return the current position of Zwave garage door."""
|
|
||||||
return self._state == "Closed"
|
|
||||||
|
|
||||||
def close_cover(self, **kwargs):
|
|
||||||
"""Close the garage door."""
|
|
||||||
self.values.primary.data = "Closed"
|
|
||||||
|
|
||||||
def open_cover(self, **kwargs):
|
|
||||||
"""Open the garage door."""
|
|
||||||
self.values.primary.data = "Opened"
|
|
|
@ -1,416 +0,0 @@
|
||||||
"""Z-Wave discovery schemas."""
|
|
||||||
from . import const
|
|
||||||
|
|
||||||
DEFAULT_VALUES_SCHEMA = {
|
|
||||||
"power": {
|
|
||||||
const.DISC_SCHEMAS: [
|
|
||||||
{
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SENSOR_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SENSOR_MULTILEVEL_POWER],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_METER],
|
|
||||||
const.DISC_INDEX: [const.INDEX_METER_POWER],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DISCOVERY_SCHEMAS = [
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "binary_sensor",
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_ENTRY_CONTROL,
|
|
||||||
const.GENERIC_TYPE_SENSOR_ALARM,
|
|
||||||
const.GENERIC_TYPE_SENSOR_BINARY,
|
|
||||||
const.GENERIC_TYPE_SWITCH_BINARY,
|
|
||||||
const.GENERIC_TYPE_METER,
|
|
||||||
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_SENSOR_NOTIFICATION,
|
|
||||||
const.GENERIC_TYPE_THERMOSTAT,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SENSOR_BINARY],
|
|
||||||
const.DISC_TYPE: const.TYPE_BOOL,
|
|
||||||
const.DISC_GENRE: const.GENRE_USER,
|
|
||||||
},
|
|
||||||
"off_delay": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_CONFIGURATION],
|
|
||||||
const.DISC_INDEX: [9],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "climate", # thermostat without COMMAND_CLASS_THERMOSTAT_MODE
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_THERMOSTAT,
|
|
||||||
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
|
|
||||||
],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [
|
|
||||||
const.SPECIFIC_TYPE_THERMOSTAT_HEATING,
|
|
||||||
const.SPECIFIC_TYPE_SETPOINT_THERMOSTAT,
|
|
||||||
const.SPECIFIC_TYPE_NOT_USED,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT]
|
|
||||||
},
|
|
||||||
"temperature": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SENSOR_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SENSOR_MULTILEVEL_TEMPERATURE],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"fan_mode": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_FAN_MODE],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"operating_state": {
|
|
||||||
const.DISC_COMMAND_CLASS: [
|
|
||||||
const.COMMAND_CLASS_THERMOSTAT_OPERATING_STATE
|
|
||||||
],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"fan_action": {
|
|
||||||
const.DISC_COMMAND_CLASS: [
|
|
||||||
const.COMMAND_CLASS_THERMOSTAT_FAN_ACTION
|
|
||||||
],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"mode": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_MODE],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "climate", # thermostat with COMMAND_CLASS_THERMOSTAT_MODE
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_THERMOSTAT,
|
|
||||||
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
|
|
||||||
],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [
|
|
||||||
const.SPECIFIC_TYPE_THERMOSTAT_GENERAL,
|
|
||||||
const.SPECIFIC_TYPE_THERMOSTAT_GENERAL_V2,
|
|
||||||
const.SPECIFIC_TYPE_SETBACK_THERMOSTAT,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_MODE]
|
|
||||||
},
|
|
||||||
"setpoint_heating": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [1],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_cooling": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [2],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_furnace": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [7],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_dry_air": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [8],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_moist_air": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [9],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_auto_changeover": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [10],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_eco_heating": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [11],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_eco_cooling": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [12],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_away_heating": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [13],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_away_cooling": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [14],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"setpoint_full_power": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_SETPOINT],
|
|
||||||
const.DISC_INDEX: [15],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"temperature": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SENSOR_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SENSOR_MULTILEVEL_TEMPERATURE],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"fan_mode": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_THERMOSTAT_FAN_MODE],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"operating_state": {
|
|
||||||
const.DISC_COMMAND_CLASS: [
|
|
||||||
const.COMMAND_CLASS_THERMOSTAT_OPERATING_STATE
|
|
||||||
],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"fan_action": {
|
|
||||||
const.DISC_COMMAND_CLASS: [
|
|
||||||
const.COMMAND_CLASS_THERMOSTAT_FAN_ACTION
|
|
||||||
],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"zxt_120_swing_mode": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_CONFIGURATION],
|
|
||||||
const.DISC_INDEX: [33],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "cover", # Rollershutter
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_ENTRY_CONTROL,
|
|
||||||
],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [
|
|
||||||
const.SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_MOTOR_MULTIPOSITION,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_BARRIER_ADDON,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_DOOR,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
|
|
||||||
const.DISC_GENRE: const.GENRE_USER,
|
|
||||||
},
|
|
||||||
"open": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SWITCH_MULTILEVEL_BRIGHT],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"close": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SWITCH_MULTILEVEL_DIM],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "cover", # Garage Door Switch
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_ENTRY_CONTROL,
|
|
||||||
],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [
|
|
||||||
const.SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_MOTOR_MULTIPOSITION,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_BARRIER_ADDON,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_DOOR,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_BINARY],
|
|
||||||
const.DISC_GENRE: const.GENRE_USER,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "cover", # Garage Door Barrier
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_ENTRY_CONTROL,
|
|
||||||
],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [
|
|
||||||
const.SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL,
|
|
||||||
const.SPECIFIC_TYPE_MOTOR_MULTIPOSITION,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_BARRIER_ADDON,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_DOOR,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_BARRIER_OPERATOR],
|
|
||||||
const.DISC_INDEX: [const.INDEX_BARRIER_OPERATOR_LABEL],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "fan",
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [const.GENERIC_TYPE_SWITCH_MULTILEVEL],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [const.SPECIFIC_TYPE_FAN_SWITCH],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SWITCH_MULTILEVEL_LEVEL],
|
|
||||||
const.DISC_TYPE: const.TYPE_BYTE,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "light",
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_SWITCH_REMOTE,
|
|
||||||
],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [
|
|
||||||
const.SPECIFIC_TYPE_POWER_SWITCH_MULTILEVEL,
|
|
||||||
const.SPECIFIC_TYPE_SCENE_SWITCH_MULTILEVEL,
|
|
||||||
const.SPECIFIC_TYPE_NOT_USED,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SWITCH_MULTILEVEL_LEVEL],
|
|
||||||
const.DISC_TYPE: const.TYPE_BYTE,
|
|
||||||
},
|
|
||||||
"dimming_duration": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_MULTILEVEL],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SWITCH_MULTILEVEL_DURATION],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_COLOR],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SWITCH_COLOR_COLOR],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"color_channels": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_COLOR],
|
|
||||||
const.DISC_INDEX: [const.INDEX_SWITCH_COLOR_CHANNELS],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "lock",
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [const.GENERIC_TYPE_ENTRY_CONTROL],
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS: [
|
|
||||||
const.SPECIFIC_TYPE_DOOR_LOCK,
|
|
||||||
const.SPECIFIC_TYPE_ADVANCED_DOOR_LOCK,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_KEYPAD_DOOR_LOCK,
|
|
||||||
const.SPECIFIC_TYPE_SECURE_LOCKBOX,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_DOOR_LOCK],
|
|
||||||
const.DISC_INDEX: [const.INDEX_DOOR_LOCK_LOCK],
|
|
||||||
},
|
|
||||||
"access_control": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_ALARM],
|
|
||||||
const.DISC_INDEX: [const.INDEX_ALARM_ACCESS_CONTROL],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"alarm_type": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_ALARM],
|
|
||||||
const.DISC_INDEX: [const.INDEX_ALARM_TYPE],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"alarm_level": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_ALARM],
|
|
||||||
const.DISC_INDEX: [const.INDEX_ALARM_LEVEL],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
"v2btze_advanced": {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_CONFIGURATION],
|
|
||||||
const.DISC_INDEX: [12],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "sensor",
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [
|
|
||||||
const.COMMAND_CLASS_SENSOR_MULTILEVEL,
|
|
||||||
const.COMMAND_CLASS_METER,
|
|
||||||
const.COMMAND_CLASS_ALARM,
|
|
||||||
const.COMMAND_CLASS_SENSOR_ALARM,
|
|
||||||
const.COMMAND_CLASS_INDICATOR,
|
|
||||||
const.COMMAND_CLASS_BATTERY,
|
|
||||||
],
|
|
||||||
const.DISC_GENRE: const.GENRE_USER,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
const.DISC_COMPONENT: "switch",
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS: [
|
|
||||||
const.GENERIC_TYPE_METER,
|
|
||||||
const.GENERIC_TYPE_SENSOR_ALARM,
|
|
||||||
const.GENERIC_TYPE_SENSOR_BINARY,
|
|
||||||
const.GENERIC_TYPE_SWITCH_BINARY,
|
|
||||||
const.GENERIC_TYPE_ENTRY_CONTROL,
|
|
||||||
const.GENERIC_TYPE_SENSOR_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_SWITCH_MULTILEVEL,
|
|
||||||
const.GENERIC_TYPE_SENSOR_NOTIFICATION,
|
|
||||||
const.GENERIC_TYPE_GENERIC_CONTROLLER,
|
|
||||||
const.GENERIC_TYPE_SWITCH_REMOTE,
|
|
||||||
const.GENERIC_TYPE_REPEATER_SLAVE,
|
|
||||||
const.GENERIC_TYPE_THERMOSTAT,
|
|
||||||
const.GENERIC_TYPE_WALL_CONTROLLER,
|
|
||||||
],
|
|
||||||
const.DISC_VALUES: dict(
|
|
||||||
DEFAULT_VALUES_SCHEMA,
|
|
||||||
**{
|
|
||||||
const.DISC_PRIMARY: {
|
|
||||||
const.DISC_COMMAND_CLASS: [const.COMMAND_CLASS_SWITCH_BINARY],
|
|
||||||
const.DISC_TYPE: const.TYPE_BOOL,
|
|
||||||
const.DISC_GENRE: const.GENRE_USER,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
|
@ -1,86 +0,0 @@
|
||||||
"""Support for Z-Wave fans."""
|
|
||||||
import math
|
|
||||||
|
|
||||||
from homeassistant.components.fan import DOMAIN, SUPPORT_SET_SPEED, FanEntity
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
from homeassistant.util.percentage import (
|
|
||||||
int_states_in_range,
|
|
||||||
percentage_to_ranged_value,
|
|
||||||
ranged_value_to_percentage,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import ZWaveDeviceEntity
|
|
||||||
|
|
||||||
SUPPORTED_FEATURES = SUPPORT_SET_SPEED
|
|
||||||
|
|
||||||
SPEED_RANGE = (1, 99) # off is not included
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave Fan from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_fan(fan):
|
|
||||||
"""Add Z-Wave Fan."""
|
|
||||||
async_add_entities([fan])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_fan", async_add_fan)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(values, **kwargs):
|
|
||||||
"""Create Z-Wave entity device."""
|
|
||||||
return ZwaveFan(values)
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveFan(ZWaveDeviceEntity, FanEntity):
|
|
||||||
"""Representation of a Z-Wave fan."""
|
|
||||||
|
|
||||||
def __init__(self, values):
|
|
||||||
"""Initialize the Z-Wave fan device."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self.update_properties()
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle data changes for node values."""
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
|
|
||||||
def set_percentage(self, percentage):
|
|
||||||
"""Set the speed percentage of the fan."""
|
|
||||||
if percentage is None:
|
|
||||||
# Value 255 tells device to return to previous value
|
|
||||||
zwave_speed = 255
|
|
||||||
elif percentage == 0:
|
|
||||||
zwave_speed = 0
|
|
||||||
else:
|
|
||||||
zwave_speed = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage))
|
|
||||||
self.node.set_dimmer(self.values.primary.value_id, zwave_speed)
|
|
||||||
|
|
||||||
def turn_on(self, percentage=None, preset_mode=None, **kwargs):
|
|
||||||
"""Turn the device on."""
|
|
||||||
self.set_percentage(percentage)
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
|
||||||
"""Turn the device off."""
|
|
||||||
self.node.set_dimmer(self.values.primary.value_id, 0)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def percentage(self):
|
|
||||||
"""Return the current speed percentage."""
|
|
||||||
return ranged_value_to_percentage(SPEED_RANGE, self._state)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def speed_count(self) -> int:
|
|
||||||
"""Return the number of speeds the fan supports."""
|
|
||||||
return int_states_in_range(SPEED_RANGE)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Flag supported features."""
|
|
||||||
return SUPPORTED_FEATURES
|
|
|
@ -1,407 +0,0 @@
|
||||||
"""Support for Z-Wave lights."""
|
|
||||||
import logging
|
|
||||||
from threading import Timer
|
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
|
||||||
ATTR_BRIGHTNESS,
|
|
||||||
ATTR_COLOR_TEMP,
|
|
||||||
ATTR_RGB_COLOR,
|
|
||||||
ATTR_RGBW_COLOR,
|
|
||||||
ATTR_TRANSITION,
|
|
||||||
COLOR_MODE_BRIGHTNESS,
|
|
||||||
COLOR_MODE_COLOR_TEMP,
|
|
||||||
COLOR_MODE_RGB,
|
|
||||||
COLOR_MODE_RGBW,
|
|
||||||
DOMAIN,
|
|
||||||
SUPPORT_TRANSITION,
|
|
||||||
LightEntity,
|
|
||||||
)
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
|
|
||||||
from . import CONF_REFRESH_DELAY, CONF_REFRESH_VALUE, ZWaveDeviceEntity, const
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
COLOR_CHANNEL_WARM_WHITE = 0x01
|
|
||||||
COLOR_CHANNEL_COLD_WHITE = 0x02
|
|
||||||
COLOR_CHANNEL_RED = 0x04
|
|
||||||
COLOR_CHANNEL_GREEN = 0x08
|
|
||||||
COLOR_CHANNEL_BLUE = 0x10
|
|
||||||
|
|
||||||
# Some bulbs have an independent warm and cool white light LEDs. These need
|
|
||||||
# to be treated differently, aka the zw098 workaround. Ensure these are added
|
|
||||||
# to DEVICE_MAPPINGS below.
|
|
||||||
# (Manufacturer ID, Product ID) from
|
|
||||||
# https://github.com/OpenZWave/open-zwave/blob/master/config/manufacturer_specific.xml
|
|
||||||
AEOTEC_ZW098_LED_BULB_LIGHT = (0x86, 0x62)
|
|
||||||
AEOTEC_ZWA001_LED_BULB_LIGHT = (0x371, 0x1)
|
|
||||||
AEOTEC_ZWA002_LED_BULB_LIGHT = (0x371, 0x2)
|
|
||||||
HANK_HKZW_RGB01_LED_BULB_LIGHT = (0x208, 0x4)
|
|
||||||
ZIPATO_RGB_BULB_2_LED_BULB_LIGHT = (0x131, 0x3)
|
|
||||||
|
|
||||||
WORKAROUND_ZW098 = "zw098"
|
|
||||||
|
|
||||||
DEVICE_MAPPINGS = {
|
|
||||||
AEOTEC_ZW098_LED_BULB_LIGHT: WORKAROUND_ZW098,
|
|
||||||
AEOTEC_ZWA001_LED_BULB_LIGHT: WORKAROUND_ZW098,
|
|
||||||
AEOTEC_ZWA002_LED_BULB_LIGHT: WORKAROUND_ZW098,
|
|
||||||
HANK_HKZW_RGB01_LED_BULB_LIGHT: WORKAROUND_ZW098,
|
|
||||||
ZIPATO_RGB_BULB_2_LED_BULB_LIGHT: WORKAROUND_ZW098,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate midpoint color temperatures for bulbs that have limited
|
|
||||||
# support for white light colors
|
|
||||||
TEMP_COLOR_MAX = 500 # mireds (inverted)
|
|
||||||
TEMP_COLOR_MIN = 154
|
|
||||||
TEMP_MID_HASS = (TEMP_COLOR_MAX - TEMP_COLOR_MIN) / 2 + TEMP_COLOR_MIN
|
|
||||||
TEMP_WARM_HASS = (TEMP_COLOR_MAX - TEMP_COLOR_MIN) / 3 * 2 + TEMP_COLOR_MIN
|
|
||||||
TEMP_COLD_HASS = (TEMP_COLOR_MAX - TEMP_COLOR_MIN) / 3 + TEMP_COLOR_MIN
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave Light from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_light(light):
|
|
||||||
"""Add Z-Wave Light."""
|
|
||||||
async_add_entities([light])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_light", async_add_light)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(node, values, node_config, **kwargs):
|
|
||||||
"""Create Z-Wave entity device."""
|
|
||||||
refresh = node_config.get(CONF_REFRESH_VALUE)
|
|
||||||
delay = node_config.get(CONF_REFRESH_DELAY)
|
|
||||||
_LOGGER.debug(
|
|
||||||
"node=%d value=%d node_config=%s CONF_REFRESH_VALUE=%s"
|
|
||||||
" CONF_REFRESH_DELAY=%s",
|
|
||||||
node.node_id,
|
|
||||||
values.primary.value_id,
|
|
||||||
node_config,
|
|
||||||
refresh,
|
|
||||||
delay,
|
|
||||||
)
|
|
||||||
|
|
||||||
if node.has_command_class(const.COMMAND_CLASS_SWITCH_COLOR):
|
|
||||||
return ZwaveColorLight(values, refresh, delay)
|
|
||||||
return ZwaveDimmer(values, refresh, delay)
|
|
||||||
|
|
||||||
|
|
||||||
def brightness_state(value):
|
|
||||||
"""Return the brightness and state."""
|
|
||||||
if value.data > 0:
|
|
||||||
return round((value.data / 99) * 255), STATE_ON
|
|
||||||
return 0, STATE_OFF
|
|
||||||
|
|
||||||
|
|
||||||
def byte_to_zwave_brightness(value):
|
|
||||||
"""Convert brightness in 0-255 scale to 0-99 scale.
|
|
||||||
|
|
||||||
`value` -- (int) Brightness byte value from 0-255.
|
|
||||||
"""
|
|
||||||
if value > 0:
|
|
||||||
return max(1, round((value / 255) * 99))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveDimmer(ZWaveDeviceEntity, LightEntity):
|
|
||||||
"""Representation of a Z-Wave dimmer."""
|
|
||||||
|
|
||||||
def __init__(self, values, refresh, delay):
|
|
||||||
"""Initialize the light."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self._brightness = None
|
|
||||||
self._state = None
|
|
||||||
self._color_mode = None
|
|
||||||
self._supported_color_modes = set()
|
|
||||||
self._supported_features = 0
|
|
||||||
self._delay = delay
|
|
||||||
self._refresh_value = refresh
|
|
||||||
self._zw098 = None
|
|
||||||
|
|
||||||
# Enable appropriate workaround flags for our device
|
|
||||||
# Make sure that we have values for the key before converting to int
|
|
||||||
if self.node.manufacturer_id.strip() and self.node.product_id.strip():
|
|
||||||
specific_sensor_key = (
|
|
||||||
int(self.node.manufacturer_id, 16),
|
|
||||||
int(self.node.product_id, 16),
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
specific_sensor_key in DEVICE_MAPPINGS
|
|
||||||
and DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_ZW098
|
|
||||||
):
|
|
||||||
_LOGGER.debug("AEOTEC ZW098 workaround enabled")
|
|
||||||
self._zw098 = 1
|
|
||||||
|
|
||||||
# Used for value change event handling
|
|
||||||
self._refreshing = False
|
|
||||||
self._timer = None
|
|
||||||
_LOGGER.debug(
|
|
||||||
"self._refreshing=%s self.delay=%s", self._refresh_value, self._delay
|
|
||||||
)
|
|
||||||
self.value_added()
|
|
||||||
self.update_properties()
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Update internal properties based on zwave values."""
|
|
||||||
# Brightness
|
|
||||||
self._brightness, self._state = brightness_state(self.values.primary)
|
|
||||||
|
|
||||||
def value_added(self):
|
|
||||||
"""Call when a new value is added to this entity."""
|
|
||||||
self._supported_color_modes = {COLOR_MODE_BRIGHTNESS}
|
|
||||||
self._color_mode = COLOR_MODE_BRIGHTNESS
|
|
||||||
if self.values.dimming_duration is not None:
|
|
||||||
self._supported_features = SUPPORT_TRANSITION
|
|
||||||
|
|
||||||
def value_changed(self):
|
|
||||||
"""Call when a value for this entity's node has changed."""
|
|
||||||
if self._refresh_value:
|
|
||||||
if self._refreshing:
|
|
||||||
self._refreshing = False
|
|
||||||
else:
|
|
||||||
|
|
||||||
def _refresh_value():
|
|
||||||
"""Use timer callback for delayed value refresh."""
|
|
||||||
self._refreshing = True
|
|
||||||
self.values.primary.refresh()
|
|
||||||
|
|
||||||
if self._timer is not None and self._timer.is_alive():
|
|
||||||
self._timer.cancel()
|
|
||||||
|
|
||||||
self._timer = Timer(self._delay, _refresh_value)
|
|
||||||
self._timer.start()
|
|
||||||
return
|
|
||||||
super().value_changed()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def brightness(self):
|
|
||||||
"""Return the brightness of this light between 0..255."""
|
|
||||||
return self._brightness
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if device is on."""
|
|
||||||
return self._state == STATE_ON
|
|
||||||
|
|
||||||
@property
|
|
||||||
def color_mode(self):
|
|
||||||
"""Return the current color mode."""
|
|
||||||
return self._color_mode
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_color_modes(self):
|
|
||||||
"""Flag supported color modes."""
|
|
||||||
return self._supported_color_modes
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Flag supported features."""
|
|
||||||
return self._supported_features
|
|
||||||
|
|
||||||
def _set_duration(self, **kwargs):
|
|
||||||
"""Set the transition time for the brightness value.
|
|
||||||
|
|
||||||
Zwave Dimming Duration values:
|
|
||||||
0x00 = instant
|
|
||||||
0x01-0x7F = 1 second to 127 seconds
|
|
||||||
0x80-0xFE = 1 minute to 127 minutes
|
|
||||||
0xFF = factory default
|
|
||||||
"""
|
|
||||||
if self.values.dimming_duration is None:
|
|
||||||
if ATTR_TRANSITION in kwargs:
|
|
||||||
_LOGGER.debug("Dimming not supported by %s", self.entity_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
if ATTR_TRANSITION not in kwargs:
|
|
||||||
self.values.dimming_duration.data = 0xFF
|
|
||||||
return
|
|
||||||
|
|
||||||
transition = kwargs[ATTR_TRANSITION]
|
|
||||||
if transition <= 127:
|
|
||||||
self.values.dimming_duration.data = int(transition)
|
|
||||||
elif transition > 7620:
|
|
||||||
self.values.dimming_duration.data = 0xFE
|
|
||||||
_LOGGER.warning("Transition clipped to 127 minutes for %s", self.entity_id)
|
|
||||||
else:
|
|
||||||
minutes = int(transition / 60)
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Transition rounded to %d minutes for %s", minutes, self.entity_id
|
|
||||||
)
|
|
||||||
self.values.dimming_duration.data = minutes + 0x7F
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
|
||||||
"""Turn the device on."""
|
|
||||||
self._set_duration(**kwargs)
|
|
||||||
|
|
||||||
# Zwave multilevel switches use a range of [0, 99] to control
|
|
||||||
# brightness. Level 255 means to set it to previous value.
|
|
||||||
if ATTR_BRIGHTNESS in kwargs:
|
|
||||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
|
||||||
brightness = byte_to_zwave_brightness(self._brightness)
|
|
||||||
else:
|
|
||||||
brightness = 255
|
|
||||||
|
|
||||||
if self.node.set_dimmer(self.values.primary.value_id, brightness):
|
|
||||||
self._state = STATE_ON
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
|
||||||
"""Turn the device off."""
|
|
||||||
self._set_duration(**kwargs)
|
|
||||||
|
|
||||||
if self.node.set_dimmer(self.values.primary.value_id, 0):
|
|
||||||
self._state = STATE_OFF
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveColorLight(ZwaveDimmer):
|
|
||||||
"""Representation of a Z-Wave color changing light."""
|
|
||||||
|
|
||||||
def __init__(self, values, refresh, delay):
|
|
||||||
"""Initialize the light."""
|
|
||||||
self._color_channels = None
|
|
||||||
self._rgb = None
|
|
||||||
self._ct = None
|
|
||||||
self._white = None
|
|
||||||
|
|
||||||
super().__init__(values, refresh, delay)
|
|
||||||
|
|
||||||
def value_added(self):
|
|
||||||
"""Call when a new value is added to this entity."""
|
|
||||||
if self.values.dimming_duration is not None:
|
|
||||||
self._supported_features = SUPPORT_TRANSITION
|
|
||||||
|
|
||||||
self._supported_color_modes = {COLOR_MODE_RGB}
|
|
||||||
self._color_mode = COLOR_MODE_RGB
|
|
||||||
if self._zw098:
|
|
||||||
self._supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
|
|
||||||
elif self._color_channels is not None and self._color_channels & (
|
|
||||||
COLOR_CHANNEL_WARM_WHITE | COLOR_CHANNEL_COLD_WHITE
|
|
||||||
):
|
|
||||||
self._supported_color_modes = {COLOR_MODE_RGBW}
|
|
||||||
self._color_mode = COLOR_MODE_RGBW
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Update internal properties based on zwave values."""
|
|
||||||
super().update_properties()
|
|
||||||
|
|
||||||
if self.values.color is None:
|
|
||||||
return
|
|
||||||
if self.values.color_channels is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Color Channels
|
|
||||||
self._color_channels = self.values.color_channels.data
|
|
||||||
|
|
||||||
# Color Data String
|
|
||||||
data = self.values.color.data
|
|
||||||
|
|
||||||
# RGB is always present in the openzwave color data string.
|
|
||||||
self._rgb = (int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16))
|
|
||||||
|
|
||||||
# Parse remaining color channels. Openzwave appends white channels
|
|
||||||
# that are present.
|
|
||||||
index = 7
|
|
||||||
|
|
||||||
# Warm white
|
|
||||||
if self._color_channels & COLOR_CHANNEL_WARM_WHITE:
|
|
||||||
warm_white = int(data[index : index + 2], 16)
|
|
||||||
index += 2
|
|
||||||
else:
|
|
||||||
warm_white = 0
|
|
||||||
|
|
||||||
# Cold white
|
|
||||||
if self._color_channels & COLOR_CHANNEL_COLD_WHITE:
|
|
||||||
cold_white = int(data[index : index + 2], 16)
|
|
||||||
index += 2
|
|
||||||
else:
|
|
||||||
cold_white = 0
|
|
||||||
|
|
||||||
# Color temperature. With the AEOTEC ZW098 bulb, only two color
|
|
||||||
# temperatures are supported. The warm and cold channel values
|
|
||||||
# indicate brightness for warm/cold color temperature.
|
|
||||||
if self._zw098:
|
|
||||||
if warm_white > 0:
|
|
||||||
self._ct = TEMP_WARM_HASS
|
|
||||||
self._color_mode = COLOR_MODE_COLOR_TEMP
|
|
||||||
elif cold_white > 0:
|
|
||||||
self._ct = TEMP_COLD_HASS
|
|
||||||
self._color_mode = COLOR_MODE_COLOR_TEMP
|
|
||||||
else:
|
|
||||||
self._color_mode = COLOR_MODE_RGB
|
|
||||||
|
|
||||||
elif self._color_channels & COLOR_CHANNEL_WARM_WHITE:
|
|
||||||
self._white = warm_white
|
|
||||||
|
|
||||||
elif self._color_channels & COLOR_CHANNEL_COLD_WHITE:
|
|
||||||
self._white = cold_white
|
|
||||||
|
|
||||||
# If no rgb channels supported, report None.
|
|
||||||
if not (
|
|
||||||
self._color_channels & COLOR_CHANNEL_RED
|
|
||||||
or self._color_channels & COLOR_CHANNEL_GREEN
|
|
||||||
or self._color_channels & COLOR_CHANNEL_BLUE
|
|
||||||
):
|
|
||||||
self._rgb = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rgb_color(self):
|
|
||||||
"""Return the rgb color."""
|
|
||||||
return self._rgb
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rgbw_color(self):
|
|
||||||
"""Return the rgbw color."""
|
|
||||||
if self._rgb is None:
|
|
||||||
return None
|
|
||||||
return (*self._rgb, self._white)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def color_temp(self):
|
|
||||||
"""Return the color temperature."""
|
|
||||||
return self._ct
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
|
||||||
"""Turn the device on."""
|
|
||||||
rgbw = None
|
|
||||||
|
|
||||||
if ATTR_COLOR_TEMP in kwargs:
|
|
||||||
# Color temperature. With the AEOTEC ZW098 bulb, only two color
|
|
||||||
# temperatures are supported. The warm and cold channel values
|
|
||||||
# indicate brightness for warm/cold color temperature.
|
|
||||||
if self._zw098:
|
|
||||||
self._color_mode = COLOR_MODE_COLOR_TEMP
|
|
||||||
if kwargs[ATTR_COLOR_TEMP] > TEMP_MID_HASS:
|
|
||||||
self._ct = TEMP_WARM_HASS
|
|
||||||
rgbw = "#000000ff00"
|
|
||||||
else:
|
|
||||||
self._ct = TEMP_COLD_HASS
|
|
||||||
rgbw = "#00000000ff"
|
|
||||||
elif ATTR_RGB_COLOR in kwargs:
|
|
||||||
self._rgb = kwargs[ATTR_RGB_COLOR]
|
|
||||||
self._white = 0
|
|
||||||
elif ATTR_RGBW_COLOR in kwargs:
|
|
||||||
self._rgb = kwargs[ATTR_RGBW_COLOR][0:3]
|
|
||||||
self._white = kwargs[ATTR_RGBW_COLOR][3]
|
|
||||||
|
|
||||||
if ATTR_RGB_COLOR in kwargs or ATTR_RGBW_COLOR in kwargs:
|
|
||||||
rgbw = "#"
|
|
||||||
for colorval in self._rgb:
|
|
||||||
rgbw += format(colorval, "02x")
|
|
||||||
if self._white is not None:
|
|
||||||
rgbw += format(self._white, "02x") + "00"
|
|
||||||
else:
|
|
||||||
rgbw += "0000"
|
|
||||||
|
|
||||||
if rgbw and self.values.color:
|
|
||||||
self.values.color.data = rgbw
|
|
||||||
|
|
||||||
super().turn_on(**kwargs)
|
|
|
@ -1,390 +0,0 @@
|
||||||
"""Support for Z-Wave door locks."""
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.components.lock import DOMAIN, LockEntity
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
|
|
||||||
from . import ZWaveDeviceEntity, const
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
ATTR_NOTIFICATION = "notification"
|
|
||||||
ATTR_LOCK_STATUS = "lock_status"
|
|
||||||
ATTR_CODE_SLOT = "code_slot"
|
|
||||||
ATTR_USERCODE = "usercode"
|
|
||||||
CONFIG_ADVANCED = "Advanced"
|
|
||||||
|
|
||||||
SERVICE_SET_USERCODE = "set_usercode"
|
|
||||||
SERVICE_GET_USERCODE = "get_usercode"
|
|
||||||
SERVICE_CLEAR_USERCODE = "clear_usercode"
|
|
||||||
|
|
||||||
POLYCONTROL = 0x10E
|
|
||||||
DANALOCK_V2_BTZE = 0x2
|
|
||||||
POLYCONTROL_DANALOCK_V2_BTZE_LOCK = (POLYCONTROL, DANALOCK_V2_BTZE)
|
|
||||||
WORKAROUND_V2BTZE = 1
|
|
||||||
WORKAROUND_DEVICE_STATE = 2
|
|
||||||
WORKAROUND_TRACK_MESSAGE = 4
|
|
||||||
WORKAROUND_ALARM_TYPE = 8
|
|
||||||
|
|
||||||
DEVICE_MAPPINGS = {
|
|
||||||
POLYCONTROL_DANALOCK_V2_BTZE_LOCK: WORKAROUND_V2BTZE,
|
|
||||||
# Kwikset 914TRL ZW500 99100-078
|
|
||||||
(0x0090, 0x440): WORKAROUND_DEVICE_STATE,
|
|
||||||
(0x0090, 0x446): WORKAROUND_DEVICE_STATE,
|
|
||||||
(0x0090, 0x238): WORKAROUND_DEVICE_STATE,
|
|
||||||
# Kwikset 888ZW500-15S Smartcode 888
|
|
||||||
(0x0090, 0x541): WORKAROUND_DEVICE_STATE,
|
|
||||||
# Kwikset 916
|
|
||||||
(0x0090, 0x0001): WORKAROUND_DEVICE_STATE,
|
|
||||||
# Kwikset Obsidian
|
|
||||||
(0x0090, 0x0742): WORKAROUND_DEVICE_STATE,
|
|
||||||
# Yale Locks
|
|
||||||
# Yale YRD210, YRD220, YRL220
|
|
||||||
(0x0129, 0x0000): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRD210, YRD220
|
|
||||||
(0x0129, 0x0209): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRL210, YRL220
|
|
||||||
(0x0129, 0x0409): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRD256
|
|
||||||
(0x0129, 0x0600): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRD110, YRD120
|
|
||||||
(0x0129, 0x0800): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRD446
|
|
||||||
(0x0129, 0x1000): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRL220
|
|
||||||
(0x0129, 0x2132): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
(0x0129, 0x3CAC): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRD210, YRD220
|
|
||||||
(0x0129, 0xAA00): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRD220
|
|
||||||
(0x0129, 0xFFFF): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRL256
|
|
||||||
(0x0129, 0x0F00): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Yale YRD220 (Older Yale products with incorrect vendor ID)
|
|
||||||
(0x0109, 0x0000): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
||||||
# Schlage BE469
|
|
||||||
(0x003B, 0x5044): WORKAROUND_DEVICE_STATE | WORKAROUND_TRACK_MESSAGE,
|
|
||||||
# Schlage FE599NX
|
|
||||||
(0x003B, 0x504C): WORKAROUND_DEVICE_STATE,
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCK_NOTIFICATION = {
|
|
||||||
"1": "Manual Lock",
|
|
||||||
"2": "Manual Unlock",
|
|
||||||
"5": "Keypad Lock",
|
|
||||||
"6": "Keypad Unlock",
|
|
||||||
"11": "Lock Jammed",
|
|
||||||
"254": "Unknown Event",
|
|
||||||
}
|
|
||||||
NOTIFICATION_RF_LOCK = "3"
|
|
||||||
NOTIFICATION_RF_UNLOCK = "4"
|
|
||||||
LOCK_NOTIFICATION[NOTIFICATION_RF_LOCK] = "RF Lock"
|
|
||||||
LOCK_NOTIFICATION[NOTIFICATION_RF_UNLOCK] = "RF Unlock"
|
|
||||||
|
|
||||||
LOCK_ALARM_TYPE = {
|
|
||||||
"9": "Deadbolt Jammed",
|
|
||||||
"16": "Unlocked by Bluetooth ",
|
|
||||||
"18": "Locked with Keypad by user ",
|
|
||||||
"19": "Unlocked with Keypad by user ",
|
|
||||||
"21": "Manually Locked ",
|
|
||||||
"22": "Manually Unlocked ",
|
|
||||||
"27": "Auto re-lock",
|
|
||||||
"33": "User deleted: ",
|
|
||||||
"112": "Master code changed or User added: ",
|
|
||||||
"113": "Duplicate PIN code: ",
|
|
||||||
"130": "RF module, power restored",
|
|
||||||
"144": "Unlocked by NFC Tag or Card by user ",
|
|
||||||
"161": "Tamper Alarm: ",
|
|
||||||
"167": "Low Battery",
|
|
||||||
"168": "Critical Battery Level",
|
|
||||||
"169": "Battery too low to operate",
|
|
||||||
}
|
|
||||||
ALARM_RF_LOCK = "24"
|
|
||||||
ALARM_RF_UNLOCK = "25"
|
|
||||||
LOCK_ALARM_TYPE[ALARM_RF_LOCK] = "Locked by RF"
|
|
||||||
LOCK_ALARM_TYPE[ALARM_RF_UNLOCK] = "Unlocked by RF"
|
|
||||||
|
|
||||||
MANUAL_LOCK_ALARM_LEVEL = {
|
|
||||||
"1": "by Key Cylinder or Inside thumb turn",
|
|
||||||
"2": "by Touch function (lock and leave)",
|
|
||||||
}
|
|
||||||
|
|
||||||
TAMPER_ALARM_LEVEL = {"1": "Too many keypresses", "2": "Cover removed"}
|
|
||||||
|
|
||||||
LOCK_STATUS = {
|
|
||||||
"1": True,
|
|
||||||
"2": False,
|
|
||||||
"3": True,
|
|
||||||
"4": False,
|
|
||||||
"5": True,
|
|
||||||
"6": False,
|
|
||||||
"9": False,
|
|
||||||
"18": True,
|
|
||||||
"19": False,
|
|
||||||
"21": True,
|
|
||||||
"22": False,
|
|
||||||
"24": True,
|
|
||||||
"25": False,
|
|
||||||
"27": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
ALARM_TYPE_STD = ["18", "19", "33", "112", "113", "144"]
|
|
||||||
|
|
||||||
SET_USERCODE_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
|
|
||||||
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
|
|
||||||
vol.Required(ATTR_USERCODE): cv.string,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
GET_USERCODE_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
|
|
||||||
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
CLEAR_USERCODE_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
|
|
||||||
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave Lock from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_lock(lock):
|
|
||||||
"""Add Z-Wave Lock."""
|
|
||||||
async_add_entities([lock])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_lock", async_add_lock)
|
|
||||||
|
|
||||||
network = hass.data[const.DATA_NETWORK]
|
|
||||||
|
|
||||||
def set_usercode(service: ServiceCall) -> None:
|
|
||||||
"""Set the usercode to index X on the lock."""
|
|
||||||
node_id = service.data.get(const.ATTR_NODE_ID)
|
|
||||||
lock_node = network.nodes[node_id]
|
|
||||||
code_slot = service.data.get(ATTR_CODE_SLOT)
|
|
||||||
usercode = service.data.get(ATTR_USERCODE)
|
|
||||||
|
|
||||||
for value in lock_node.get_values(
|
|
||||||
class_id=const.COMMAND_CLASS_USER_CODE
|
|
||||||
).values():
|
|
||||||
if value.index != code_slot:
|
|
||||||
continue
|
|
||||||
if len(str(usercode)) < 4:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Invalid code provided: (%s) "
|
|
||||||
"usercode must be at least 4 and at most"
|
|
||||||
" %s digits",
|
|
||||||
usercode,
|
|
||||||
len(value.data),
|
|
||||||
)
|
|
||||||
break
|
|
||||||
value.data = str(usercode)
|
|
||||||
break
|
|
||||||
|
|
||||||
def get_usercode(service: ServiceCall) -> None:
|
|
||||||
"""Get a usercode at index X on the lock."""
|
|
||||||
node_id = service.data.get(const.ATTR_NODE_ID)
|
|
||||||
lock_node = network.nodes[node_id]
|
|
||||||
code_slot = service.data.get(ATTR_CODE_SLOT)
|
|
||||||
|
|
||||||
for value in lock_node.get_values(
|
|
||||||
class_id=const.COMMAND_CLASS_USER_CODE
|
|
||||||
).values():
|
|
||||||
if value.index != code_slot:
|
|
||||||
continue
|
|
||||||
_LOGGER.info("Usercode at slot %s is: %s", value.index, value.data)
|
|
||||||
break
|
|
||||||
|
|
||||||
def clear_usercode(service: ServiceCall) -> None:
|
|
||||||
"""Set usercode to slot X on the lock."""
|
|
||||||
node_id = service.data.get(const.ATTR_NODE_ID)
|
|
||||||
lock_node = network.nodes[node_id]
|
|
||||||
code_slot = service.data.get(ATTR_CODE_SLOT)
|
|
||||||
data = ""
|
|
||||||
|
|
||||||
for value in lock_node.get_values(
|
|
||||||
class_id=const.COMMAND_CLASS_USER_CODE
|
|
||||||
).values():
|
|
||||||
if value.index != code_slot:
|
|
||||||
continue
|
|
||||||
for i in range(len(value.data)):
|
|
||||||
data += "\0"
|
|
||||||
i += 1
|
|
||||||
_LOGGER.debug("Data to clear lock: %s", data)
|
|
||||||
value.data = data
|
|
||||||
_LOGGER.info("Usercode at slot %s is cleared", value.index)
|
|
||||||
break
|
|
||||||
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, SERVICE_SET_USERCODE, set_usercode, schema=SET_USERCODE_SCHEMA
|
|
||||||
)
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, SERVICE_GET_USERCODE, get_usercode, schema=GET_USERCODE_SCHEMA
|
|
||||||
)
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, SERVICE_CLEAR_USERCODE, clear_usercode, schema=CLEAR_USERCODE_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(node, values, **kwargs):
|
|
||||||
"""Create Z-Wave entity device."""
|
|
||||||
return ZwaveLock(values)
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveLock(ZWaveDeviceEntity, LockEntity):
|
|
||||||
"""Representation of a Z-Wave Lock."""
|
|
||||||
|
|
||||||
def __init__(self, values):
|
|
||||||
"""Initialize the Z-Wave lock device."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self._state = None
|
|
||||||
self._notification = None
|
|
||||||
self._lock_status = None
|
|
||||||
self._v2btze = None
|
|
||||||
self._state_workaround = False
|
|
||||||
self._track_message_workaround = False
|
|
||||||
self._previous_message = None
|
|
||||||
self._alarm_type_workaround = False
|
|
||||||
|
|
||||||
# Enable appropriate workaround flags for our device
|
|
||||||
# Make sure that we have values for the key before converting to int
|
|
||||||
if self.node.manufacturer_id.strip() and self.node.product_id.strip():
|
|
||||||
specific_sensor_key = (
|
|
||||||
int(self.node.manufacturer_id, 16),
|
|
||||||
int(self.node.product_id, 16),
|
|
||||||
)
|
|
||||||
if specific_sensor_key in DEVICE_MAPPINGS:
|
|
||||||
workaround = DEVICE_MAPPINGS[specific_sensor_key]
|
|
||||||
if workaround & WORKAROUND_V2BTZE:
|
|
||||||
self._v2btze = 1
|
|
||||||
_LOGGER.debug("Polycontrol Danalock v2 BTZE workaround enabled")
|
|
||||||
if workaround & WORKAROUND_DEVICE_STATE:
|
|
||||||
self._state_workaround = True
|
|
||||||
_LOGGER.debug("Notification device state workaround enabled")
|
|
||||||
if workaround & WORKAROUND_TRACK_MESSAGE:
|
|
||||||
self._track_message_workaround = True
|
|
||||||
_LOGGER.debug("Message tracking workaround enabled")
|
|
||||||
if workaround & WORKAROUND_ALARM_TYPE:
|
|
||||||
self._alarm_type_workaround = True
|
|
||||||
_LOGGER.debug("Alarm Type device state workaround enabled")
|
|
||||||
self.update_properties()
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle data changes for node values."""
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
_LOGGER.debug("lock state set to %s", self._state)
|
|
||||||
if self.values.access_control:
|
|
||||||
notification_data = self.values.access_control.data
|
|
||||||
self._notification = LOCK_NOTIFICATION.get(str(notification_data))
|
|
||||||
if self._state_workaround:
|
|
||||||
self._state = LOCK_STATUS.get(str(notification_data))
|
|
||||||
_LOGGER.debug("workaround: lock state set to %s", self._state)
|
|
||||||
if (
|
|
||||||
self._v2btze
|
|
||||||
and self.values.v2btze_advanced
|
|
||||||
and self.values.v2btze_advanced.data == CONFIG_ADVANCED
|
|
||||||
):
|
|
||||||
self._state = LOCK_STATUS.get(str(notification_data))
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Lock state set from Access Control value and is %s, get=%s",
|
|
||||||
str(notification_data),
|
|
||||||
self.state,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._track_message_workaround:
|
|
||||||
this_message = self.node.stats["lastReceivedMessage"][5]
|
|
||||||
|
|
||||||
if this_message == const.COMMAND_CLASS_DOOR_LOCK:
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
_LOGGER.debug("set state to %s based on message tracking", self._state)
|
|
||||||
if self._previous_message == const.COMMAND_CLASS_DOOR_LOCK:
|
|
||||||
if self._state:
|
|
||||||
self._notification = LOCK_NOTIFICATION[NOTIFICATION_RF_LOCK]
|
|
||||||
self._lock_status = LOCK_ALARM_TYPE[ALARM_RF_LOCK]
|
|
||||||
else:
|
|
||||||
self._notification = LOCK_NOTIFICATION[NOTIFICATION_RF_UNLOCK]
|
|
||||||
self._lock_status = LOCK_ALARM_TYPE[ALARM_RF_UNLOCK]
|
|
||||||
return
|
|
||||||
|
|
||||||
self._previous_message = this_message
|
|
||||||
|
|
||||||
if not self.values.alarm_type:
|
|
||||||
return
|
|
||||||
|
|
||||||
alarm_type = self.values.alarm_type.data
|
|
||||||
if self.values.alarm_level:
|
|
||||||
alarm_level = self.values.alarm_level.data
|
|
||||||
else:
|
|
||||||
alarm_level = None
|
|
||||||
|
|
||||||
if not alarm_type:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._alarm_type_workaround:
|
|
||||||
self._state = LOCK_STATUS.get(str(alarm_type))
|
|
||||||
_LOGGER.debug(
|
|
||||||
"workaround: lock state set to %s -- alarm type: %s",
|
|
||||||
self._state,
|
|
||||||
str(alarm_type),
|
|
||||||
)
|
|
||||||
|
|
||||||
if alarm_type == 21:
|
|
||||||
self._lock_status = (
|
|
||||||
f"{LOCK_ALARM_TYPE.get(str(alarm_type))}"
|
|
||||||
f"{MANUAL_LOCK_ALARM_LEVEL.get(str(alarm_level))}"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if str(alarm_type) in ALARM_TYPE_STD:
|
|
||||||
self._lock_status = f"{LOCK_ALARM_TYPE.get(str(alarm_type))}{alarm_level}"
|
|
||||||
return
|
|
||||||
if alarm_type == 161:
|
|
||||||
self._lock_status = (
|
|
||||||
f"{LOCK_ALARM_TYPE.get(str(alarm_type))}"
|
|
||||||
f"{TAMPER_ALARM_LEVEL.get(str(alarm_level))}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
if alarm_type != 0:
|
|
||||||
self._lock_status = LOCK_ALARM_TYPE.get(str(alarm_type))
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_locked(self):
|
|
||||||
"""Return true if device is locked."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
def lock(self, **kwargs):
|
|
||||||
"""Lock the device."""
|
|
||||||
self.values.primary.data = True
|
|
||||||
|
|
||||||
def unlock(self, **kwargs):
|
|
||||||
"""Unlock the device."""
|
|
||||||
self.values.primary.data = False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device specific state attributes."""
|
|
||||||
data = super().extra_state_attributes
|
|
||||||
if self._notification:
|
|
||||||
data[ATTR_NOTIFICATION] = self._notification
|
|
||||||
if self._lock_status:
|
|
||||||
data[ATTR_LOCK_STATUS] = self._lock_status
|
|
||||||
return data
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"domain": "zwave",
|
|
||||||
"name": "Z-Wave (deprecated)",
|
|
||||||
"config_flow": true,
|
|
||||||
"documentation": "https://www.home-assistant.io/integrations/zwave",
|
|
||||||
"requirements": ["homeassistant-pyozw==0.1.10", "pydispatcher==2.0.5"],
|
|
||||||
"codeowners": ["@home-assistant/z-wave"],
|
|
||||||
"iot_class": "local_push"
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
"""Handle migration from legacy Z-Wave to OpenZWave and Z-Wave JS."""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, TypedDict, cast
|
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.device_registry import async_get as async_get_device_registry
|
|
||||||
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
|
|
||||||
from homeassistant.helpers.singleton import singleton
|
|
||||||
from homeassistant.helpers.storage import Store
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
|
||||||
from .util import node_device_id_and_name
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from . import ZWaveDeviceEntityValues
|
|
||||||
|
|
||||||
LEGACY_ZWAVE_MIGRATION = f"{DOMAIN}_legacy_zwave_migration"
|
|
||||||
STORAGE_WRITE_DELAY = 30
|
|
||||||
STORAGE_KEY = f"{DOMAIN}.legacy_zwave_migration"
|
|
||||||
STORAGE_VERSION = 1
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveMigrationData(TypedDict):
|
|
||||||
"""Represent the Z-Wave migration data dict."""
|
|
||||||
|
|
||||||
node_id: int
|
|
||||||
node_instance: int
|
|
||||||
command_class: int
|
|
||||||
command_class_label: str
|
|
||||||
value_index: int
|
|
||||||
device_id: str
|
|
||||||
domain: str
|
|
||||||
entity_id: str
|
|
||||||
unique_id: str
|
|
||||||
unit_of_measurement: str | None
|
|
||||||
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_is_ozw_migrated(hass):
|
|
||||||
"""Return True if migration to ozw is done."""
|
|
||||||
ozw_config_entries = hass.config_entries.async_entries("ozw")
|
|
||||||
if not ozw_config_entries:
|
|
||||||
return False
|
|
||||||
|
|
||||||
ozw_config_entry = ozw_config_entries[0] # only one ozw entry is allowed
|
|
||||||
migrated = bool(ozw_config_entry.data.get("migrated"))
|
|
||||||
return migrated
|
|
||||||
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_is_zwave_js_migrated(hass):
|
|
||||||
"""Return True if migration to Z-Wave JS is done."""
|
|
||||||
zwave_js_config_entries = hass.config_entries.async_entries("zwave_js")
|
|
||||||
if not zwave_js_config_entries:
|
|
||||||
return False
|
|
||||||
|
|
||||||
migrated = any(
|
|
||||||
config_entry.data.get("migrated") for config_entry in zwave_js_config_entries
|
|
||||||
)
|
|
||||||
return migrated
|
|
||||||
|
|
||||||
|
|
||||||
async def async_add_migration_entity_value(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
entity_id: str,
|
|
||||||
entity_values: ZWaveDeviceEntityValues,
|
|
||||||
) -> None:
|
|
||||||
"""Add Z-Wave entity value for legacy Z-Wave migration."""
|
|
||||||
migration_handler: LegacyZWaveMigration = await get_legacy_zwave_migration(hass)
|
|
||||||
migration_handler.add_entity_value(entity_id, entity_values)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_get_migration_data(
|
|
||||||
hass: HomeAssistant, config_entry: ConfigEntry
|
|
||||||
) -> dict[str, ZWaveMigrationData]:
|
|
||||||
"""Return Z-Wave migration data."""
|
|
||||||
migration_handler: LegacyZWaveMigration = await get_legacy_zwave_migration(hass)
|
|
||||||
return await migration_handler.get_data(config_entry)
|
|
||||||
|
|
||||||
|
|
||||||
@singleton(LEGACY_ZWAVE_MIGRATION)
|
|
||||||
async def get_legacy_zwave_migration(hass: HomeAssistant) -> LegacyZWaveMigration:
|
|
||||||
"""Return legacy Z-Wave migration handler."""
|
|
||||||
migration_handler = LegacyZWaveMigration(hass)
|
|
||||||
await migration_handler.load_data()
|
|
||||||
return migration_handler
|
|
||||||
|
|
||||||
|
|
||||||
class LegacyZWaveMigration:
|
|
||||||
"""Handle the migration from zwave to ozw and zwave_js."""
|
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant) -> None:
|
|
||||||
"""Set up migration instance."""
|
|
||||||
self._hass = hass
|
|
||||||
self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
|
|
||||||
self._data: dict[str, dict[str, ZWaveMigrationData]] = {}
|
|
||||||
|
|
||||||
async def load_data(self) -> None:
|
|
||||||
"""Load Z-Wave migration data."""
|
|
||||||
stored = cast(dict, await self._store.async_load())
|
|
||||||
if stored:
|
|
||||||
self._data = stored
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def save_data(
|
|
||||||
self, config_entry_id: str, entity_id: str, data: ZWaveMigrationData
|
|
||||||
) -> None:
|
|
||||||
"""Save Z-Wave migration data."""
|
|
||||||
if config_entry_id not in self._data:
|
|
||||||
self._data[config_entry_id] = {}
|
|
||||||
self._data[config_entry_id][entity_id] = data
|
|
||||||
self._store.async_delay_save(self._data_to_save, STORAGE_WRITE_DELAY)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _data_to_save(self) -> dict[str, dict[str, ZWaveMigrationData]]:
|
|
||||||
"""Return data to save."""
|
|
||||||
return self._data
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def add_entity_value(
|
|
||||||
self,
|
|
||||||
entity_id: str,
|
|
||||||
entity_values: ZWaveDeviceEntityValues,
|
|
||||||
) -> None:
|
|
||||||
"""Add info for one entity and Z-Wave value."""
|
|
||||||
ent_reg = async_get_entity_registry(self._hass)
|
|
||||||
dev_reg = async_get_device_registry(self._hass)
|
|
||||||
|
|
||||||
node = entity_values.primary.node
|
|
||||||
entity_entry = ent_reg.async_get(entity_id)
|
|
||||||
assert entity_entry
|
|
||||||
device_identifier, _ = node_device_id_and_name(
|
|
||||||
node, entity_values.primary.instance
|
|
||||||
)
|
|
||||||
device_entry = dev_reg.async_get_device({device_identifier}, set())
|
|
||||||
assert device_entry
|
|
||||||
|
|
||||||
# Normalize unit of measurement.
|
|
||||||
if unit := entity_entry.unit_of_measurement:
|
|
||||||
unit = unit.lower()
|
|
||||||
if unit == "":
|
|
||||||
unit = None
|
|
||||||
|
|
||||||
data: ZWaveMigrationData = {
|
|
||||||
"node_id": node.node_id,
|
|
||||||
"node_instance": entity_values.primary.instance,
|
|
||||||
"command_class": entity_values.primary.command_class,
|
|
||||||
"command_class_label": entity_values.primary.label,
|
|
||||||
"value_index": entity_values.primary.index,
|
|
||||||
"device_id": device_entry.id,
|
|
||||||
"domain": entity_entry.domain,
|
|
||||||
"entity_id": entity_id,
|
|
||||||
"unique_id": entity_entry.unique_id,
|
|
||||||
"unit_of_measurement": unit,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.save_data(entity_entry.config_entry_id, entity_id, data)
|
|
||||||
|
|
||||||
async def get_data(
|
|
||||||
self, config_entry: ConfigEntry
|
|
||||||
) -> dict[str, ZWaveMigrationData]:
|
|
||||||
"""Return Z-Wave migration data."""
|
|
||||||
await self.load_data()
|
|
||||||
data = self._data.get(config_entry.entry_id)
|
|
||||||
return data or {}
|
|
|
@ -1,381 +0,0 @@
|
||||||
"""Entity class that represents Z-Wave node."""
|
|
||||||
# pylint: disable=import-error
|
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
from itertools import count
|
|
||||||
|
|
||||||
from homeassistant.const import (
|
|
||||||
ATTR_BATTERY_LEVEL,
|
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
ATTR_VIA_DEVICE,
|
|
||||||
ATTR_WAKEUP,
|
|
||||||
)
|
|
||||||
from homeassistant.core import callback
|
|
||||||
from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg
|
|
||||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
|
||||||
from homeassistant.helpers.entity_registry import async_get_registry
|
|
||||||
|
|
||||||
from .const import (
|
|
||||||
ATTR_BASIC_LEVEL,
|
|
||||||
ATTR_NODE_ID,
|
|
||||||
ATTR_SCENE_DATA,
|
|
||||||
ATTR_SCENE_ID,
|
|
||||||
COMMAND_CLASS_CENTRAL_SCENE,
|
|
||||||
COMMAND_CLASS_VERSION,
|
|
||||||
COMMAND_CLASS_WAKE_UP,
|
|
||||||
DOMAIN,
|
|
||||||
EVENT_NODE_EVENT,
|
|
||||||
EVENT_SCENE_ACTIVATED,
|
|
||||||
)
|
|
||||||
from .util import is_node_parsed, node_device_id_and_name, node_name
|
|
||||||
|
|
||||||
ATTR_QUERY_STAGE = "query_stage"
|
|
||||||
ATTR_AWAKE = "is_awake"
|
|
||||||
ATTR_READY = "is_ready"
|
|
||||||
ATTR_FAILED = "is_failed"
|
|
||||||
ATTR_PRODUCT_NAME = "product_name"
|
|
||||||
ATTR_MANUFACTURER_NAME = "manufacturer_name"
|
|
||||||
ATTR_NODE_NAME = "node_name"
|
|
||||||
ATTR_APPLICATION_VERSION = "application_version"
|
|
||||||
|
|
||||||
STAGE_COMPLETE = "Complete"
|
|
||||||
|
|
||||||
_REQUIRED_ATTRIBUTES = [
|
|
||||||
ATTR_QUERY_STAGE,
|
|
||||||
ATTR_AWAKE,
|
|
||||||
ATTR_READY,
|
|
||||||
ATTR_FAILED,
|
|
||||||
"is_info_received",
|
|
||||||
"max_baud_rate",
|
|
||||||
"is_zwave_plus",
|
|
||||||
]
|
|
||||||
_OPTIONAL_ATTRIBUTES = ["capabilities", "neighbors", "location"]
|
|
||||||
_COMM_ATTRIBUTES = [
|
|
||||||
"sentCnt",
|
|
||||||
"sentFailed",
|
|
||||||
"retries",
|
|
||||||
"receivedCnt",
|
|
||||||
"receivedDups",
|
|
||||||
"receivedUnsolicited",
|
|
||||||
"sentTS",
|
|
||||||
"receivedTS",
|
|
||||||
"lastRequestRTT",
|
|
||||||
"averageRequestRTT",
|
|
||||||
"lastResponseRTT",
|
|
||||||
"averageResponseRTT",
|
|
||||||
]
|
|
||||||
ATTRIBUTES = _REQUIRED_ATTRIBUTES + _OPTIONAL_ATTRIBUTES
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveBaseEntity(Entity):
|
|
||||||
"""Base class for Z-Wave Node and Value entities."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Initialize the base Z-Wave class."""
|
|
||||||
self._update_scheduled = False
|
|
||||||
|
|
||||||
def maybe_schedule_update(self):
|
|
||||||
"""Maybe schedule state update.
|
|
||||||
|
|
||||||
If value changed after device was created but before setup_platform
|
|
||||||
was called - skip updating state.
|
|
||||||
"""
|
|
||||||
if self.hass and not self._update_scheduled:
|
|
||||||
self.hass.add_job(self._schedule_update)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _schedule_update(self):
|
|
||||||
"""Schedule delayed update."""
|
|
||||||
if self._update_scheduled:
|
|
||||||
return
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def do_update():
|
|
||||||
"""Really update."""
|
|
||||||
self.async_write_ha_state()
|
|
||||||
self._update_scheduled = False
|
|
||||||
|
|
||||||
self._update_scheduled = True
|
|
||||||
self.hass.loop.call_later(0.1, do_update)
|
|
||||||
|
|
||||||
def try_remove_and_add(self):
|
|
||||||
"""Remove this entity and add it back."""
|
|
||||||
|
|
||||||
async def _async_remove_and_add():
|
|
||||||
await self.async_remove(force_remove=True)
|
|
||||||
self.entity_id = None
|
|
||||||
await self.platform.async_add_entities([self])
|
|
||||||
|
|
||||||
if self.hass and self.platform:
|
|
||||||
self.hass.add_job(_async_remove_and_add)
|
|
||||||
|
|
||||||
async def node_removed(self):
|
|
||||||
"""Call when a node is removed from the Z-Wave network."""
|
|
||||||
await self.async_remove(force_remove=True)
|
|
||||||
|
|
||||||
registry = await async_get_registry(self.hass)
|
|
||||||
if self.entity_id not in registry.entities:
|
|
||||||
return
|
|
||||||
|
|
||||||
registry.async_remove(self.entity_id)
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveNodeEntity(ZWaveBaseEntity):
|
|
||||||
"""Representation of a Z-Wave node."""
|
|
||||||
|
|
||||||
def __init__(self, node, network):
|
|
||||||
"""Initialize node."""
|
|
||||||
super().__init__()
|
|
||||||
from openzwave.network import ZWaveNetwork
|
|
||||||
from pydispatch import dispatcher
|
|
||||||
|
|
||||||
self._network = network
|
|
||||||
self.node = node
|
|
||||||
self.node_id = self.node.node_id
|
|
||||||
self._name = node_name(self.node)
|
|
||||||
self._product_name = node.product_name
|
|
||||||
self._manufacturer_name = node.manufacturer_name
|
|
||||||
self._unique_id = self._compute_unique_id()
|
|
||||||
self._application_version = None
|
|
||||||
self._attributes = {}
|
|
||||||
self.wakeup_interval = None
|
|
||||||
self.location = None
|
|
||||||
self.battery_level = None
|
|
||||||
dispatcher.connect(
|
|
||||||
self.network_node_value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED
|
|
||||||
)
|
|
||||||
dispatcher.connect(self.network_node_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
|
|
||||||
dispatcher.connect(self.network_node_changed, ZWaveNetwork.SIGNAL_NODE)
|
|
||||||
dispatcher.connect(self.network_node_changed, ZWaveNetwork.SIGNAL_NOTIFICATION)
|
|
||||||
dispatcher.connect(self.network_node_event, ZWaveNetwork.SIGNAL_NODE_EVENT)
|
|
||||||
dispatcher.connect(
|
|
||||||
self.network_scene_activated, ZWaveNetwork.SIGNAL_SCENE_EVENT
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self):
|
|
||||||
"""Return unique ID of Z-wave node."""
|
|
||||||
return self._unique_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self) -> DeviceInfo:
|
|
||||||
"""Return device information."""
|
|
||||||
identifier, name = node_device_id_and_name(self.node)
|
|
||||||
info = DeviceInfo(
|
|
||||||
identifiers={identifier},
|
|
||||||
manufacturer=self.node.manufacturer_name,
|
|
||||||
model=self.node.product_name,
|
|
||||||
name=name,
|
|
||||||
)
|
|
||||||
if self.node_id > 1:
|
|
||||||
info[ATTR_VIA_DEVICE] = (DOMAIN, 1)
|
|
||||||
return info
|
|
||||||
|
|
||||||
def maybe_update_application_version(self, value):
|
|
||||||
"""Update application version if value is a Command Class Version, Application Value."""
|
|
||||||
if (
|
|
||||||
value
|
|
||||||
and value.command_class == COMMAND_CLASS_VERSION
|
|
||||||
and value.label == "Application Version"
|
|
||||||
):
|
|
||||||
self._application_version = value.data
|
|
||||||
|
|
||||||
def network_node_value_added(self, node=None, value=None, args=None):
|
|
||||||
"""Handle a added value to a none on the network."""
|
|
||||||
if node and node.node_id != self.node_id:
|
|
||||||
return
|
|
||||||
if args is not None and "nodeId" in args and args["nodeId"] != self.node_id:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.maybe_update_application_version(value)
|
|
||||||
|
|
||||||
def network_node_changed(self, node=None, value=None, args=None):
|
|
||||||
"""Handle a changed node on the network."""
|
|
||||||
if node and node.node_id != self.node_id:
|
|
||||||
return
|
|
||||||
if args is not None and "nodeId" in args and args["nodeId"] != self.node_id:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Process central scene activation
|
|
||||||
if value is not None and value.command_class == COMMAND_CLASS_CENTRAL_SCENE:
|
|
||||||
self.central_scene_activated(value.index, value.data)
|
|
||||||
|
|
||||||
self.maybe_update_application_version(value)
|
|
||||||
|
|
||||||
self.node_changed()
|
|
||||||
|
|
||||||
def get_node_statistics(self):
|
|
||||||
"""Retrieve statistics from the node."""
|
|
||||||
return self._network.manager.getNodeStatistics(
|
|
||||||
self._network.home_id, self.node_id
|
|
||||||
)
|
|
||||||
|
|
||||||
def node_changed(self):
|
|
||||||
"""Update node properties."""
|
|
||||||
attributes = {}
|
|
||||||
stats = self.get_node_statistics()
|
|
||||||
for attr in ATTRIBUTES:
|
|
||||||
value = getattr(self.node, attr)
|
|
||||||
if attr in _REQUIRED_ATTRIBUTES or value:
|
|
||||||
attributes[attr] = value
|
|
||||||
|
|
||||||
for attr in _COMM_ATTRIBUTES:
|
|
||||||
attributes[attr] = stats[attr]
|
|
||||||
|
|
||||||
if self.node.can_wake_up():
|
|
||||||
for value in self.node.get_values(COMMAND_CLASS_WAKE_UP).values():
|
|
||||||
if value.index != 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.wakeup_interval = value.data
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.wakeup_interval = None
|
|
||||||
|
|
||||||
self.battery_level = self.node.get_battery_level()
|
|
||||||
self._product_name = self.node.product_name
|
|
||||||
self._manufacturer_name = self.node.manufacturer_name
|
|
||||||
self._name = node_name(self.node)
|
|
||||||
self._attributes = attributes
|
|
||||||
|
|
||||||
if not self._unique_id:
|
|
||||||
self._unique_id = self._compute_unique_id()
|
|
||||||
if self._unique_id:
|
|
||||||
# Node info parsed. Remove and re-add
|
|
||||||
self.try_remove_and_add()
|
|
||||||
|
|
||||||
self.maybe_schedule_update()
|
|
||||||
|
|
||||||
async def node_renamed(self, update_ids=False):
|
|
||||||
"""Rename the node and update any IDs."""
|
|
||||||
identifier, self._name = node_device_id_and_name(self.node)
|
|
||||||
# Set the name in the devices. If they're customised
|
|
||||||
# the customisation will not be stored as name and will stick.
|
|
||||||
dev_reg = await get_dev_reg(self.hass)
|
|
||||||
device = dev_reg.async_get_device(identifiers={identifier})
|
|
||||||
dev_reg.async_update_device(device.id, name=self._name)
|
|
||||||
# update sub-devices too
|
|
||||||
for i in count(2):
|
|
||||||
identifier, new_name = node_device_id_and_name(self.node, i)
|
|
||||||
device = dev_reg.async_get_device(identifiers={identifier})
|
|
||||||
if not device:
|
|
||||||
break
|
|
||||||
dev_reg.async_update_device(device.id, name=new_name)
|
|
||||||
|
|
||||||
# Update entity ID.
|
|
||||||
if update_ids:
|
|
||||||
ent_reg = await async_get_registry(self.hass)
|
|
||||||
new_entity_id = ent_reg.async_generate_entity_id(
|
|
||||||
DOMAIN, self._name, self.platform.entities.keys() - {self.entity_id}
|
|
||||||
)
|
|
||||||
if new_entity_id != self.entity_id:
|
|
||||||
# Don't change the name attribute, it will be None unless
|
|
||||||
# customised and if it's been customised, keep the
|
|
||||||
# customisation.
|
|
||||||
ent_reg.async_update_entity(self.entity_id, new_entity_id=new_entity_id)
|
|
||||||
return
|
|
||||||
# else for the above two ifs, update if not using update_entity
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
def network_node_event(self, node, value):
|
|
||||||
"""Handle a node activated event on the network."""
|
|
||||||
if node.node_id == self.node.node_id:
|
|
||||||
self.node_event(value)
|
|
||||||
|
|
||||||
def node_event(self, value):
|
|
||||||
"""Handle a node activated event for this node."""
|
|
||||||
if self.hass is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.hass.bus.fire(
|
|
||||||
EVENT_NODE_EVENT,
|
|
||||||
{
|
|
||||||
ATTR_ENTITY_ID: self.entity_id,
|
|
||||||
ATTR_NODE_ID: self.node.node_id,
|
|
||||||
ATTR_BASIC_LEVEL: value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def network_scene_activated(self, node, scene_id):
|
|
||||||
"""Handle a scene activated event on the network."""
|
|
||||||
if node.node_id == self.node.node_id:
|
|
||||||
self.scene_activated(scene_id)
|
|
||||||
|
|
||||||
def scene_activated(self, scene_id):
|
|
||||||
"""Handle an activated scene for this node."""
|
|
||||||
if self.hass is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.hass.bus.fire(
|
|
||||||
EVENT_SCENE_ACTIVATED,
|
|
||||||
{
|
|
||||||
ATTR_ENTITY_ID: self.entity_id,
|
|
||||||
ATTR_NODE_ID: self.node.node_id,
|
|
||||||
ATTR_SCENE_ID: scene_id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def central_scene_activated(self, scene_id, scene_data):
|
|
||||||
"""Handle an activated central scene for this node."""
|
|
||||||
if self.hass is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.hass.bus.fire(
|
|
||||||
EVENT_SCENE_ACTIVATED,
|
|
||||||
{
|
|
||||||
ATTR_ENTITY_ID: self.entity_id,
|
|
||||||
ATTR_NODE_ID: self.node_id,
|
|
||||||
ATTR_SCENE_ID: scene_id,
|
|
||||||
ATTR_SCENE_DATA: scene_data,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self):
|
|
||||||
"""Return the state."""
|
|
||||||
if ATTR_READY not in self._attributes:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self._attributes[ATTR_FAILED]:
|
|
||||||
return "dead"
|
|
||||||
if self._attributes[ATTR_QUERY_STAGE] != "Complete":
|
|
||||||
return "initializing"
|
|
||||||
if not self._attributes[ATTR_AWAKE]:
|
|
||||||
return "sleeping"
|
|
||||||
if self._attributes[ATTR_READY]:
|
|
||||||
return "ready"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def should_poll(self):
|
|
||||||
"""No polling needed."""
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the device."""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device specific state attributes."""
|
|
||||||
attrs = {
|
|
||||||
ATTR_NODE_ID: self.node_id,
|
|
||||||
ATTR_NODE_NAME: self._name,
|
|
||||||
ATTR_MANUFACTURER_NAME: self._manufacturer_name,
|
|
||||||
ATTR_PRODUCT_NAME: self._product_name,
|
|
||||||
}
|
|
||||||
attrs.update(self._attributes)
|
|
||||||
if self.battery_level is not None:
|
|
||||||
attrs[ATTR_BATTERY_LEVEL] = self.battery_level
|
|
||||||
if self.wakeup_interval is not None:
|
|
||||||
attrs[ATTR_WAKEUP] = self.wakeup_interval
|
|
||||||
if self._application_version is not None:
|
|
||||||
attrs[ATTR_APPLICATION_VERSION] = self._application_version
|
|
||||||
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
def _compute_unique_id(self):
|
|
||||||
if is_node_parsed(self.node) or self.node.is_ready:
|
|
||||||
return f"node-{self.node_id}"
|
|
||||||
return None
|
|
|
@ -1,124 +0,0 @@
|
||||||
"""Support for Z-Wave sensors."""
|
|
||||||
from homeassistant.components.sensor import DOMAIN, SensorDeviceClass, SensorEntity
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
|
|
||||||
from . import ZWaveDeviceEntity, const
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave Sensor from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_sensor(sensor):
|
|
||||||
"""Add Z-Wave Sensor."""
|
|
||||||
async_add_entities([sensor])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_sensor", async_add_sensor)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(node, values, **kwargs):
|
|
||||||
"""Create Z-Wave entity device."""
|
|
||||||
# Generic Device mappings
|
|
||||||
if values.primary.command_class == const.COMMAND_CLASS_BATTERY:
|
|
||||||
return ZWaveBatterySensor(values)
|
|
||||||
if node.has_command_class(const.COMMAND_CLASS_SENSOR_MULTILEVEL):
|
|
||||||
return ZWaveMultilevelSensor(values)
|
|
||||||
if (
|
|
||||||
node.has_command_class(const.COMMAND_CLASS_METER)
|
|
||||||
and values.primary.type == const.TYPE_DECIMAL
|
|
||||||
):
|
|
||||||
return ZWaveMultilevelSensor(values)
|
|
||||||
if node.has_command_class(const.COMMAND_CLASS_ALARM) or node.has_command_class(
|
|
||||||
const.COMMAND_CLASS_SENSOR_ALARM
|
|
||||||
):
|
|
||||||
return ZWaveAlarmSensor(values)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveSensor(ZWaveDeviceEntity, SensorEntity):
|
|
||||||
"""Representation of a Z-Wave sensor."""
|
|
||||||
|
|
||||||
def __init__(self, values):
|
|
||||||
"""Initialize the sensor."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self.update_properties()
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle the data changes for node values."""
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
self._units = self.values.primary.units
|
|
||||||
|
|
||||||
@property
|
|
||||||
def force_update(self):
|
|
||||||
"""Return force_update."""
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self):
|
|
||||||
"""Return the state of the sensor."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_unit_of_measurement(self):
|
|
||||||
"""Return the unit of measurement the value is expressed in."""
|
|
||||||
return self._units
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveMultilevelSensor(ZWaveSensor):
|
|
||||||
"""Representation of a multi level sensor Z-Wave sensor."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self):
|
|
||||||
"""Return the state of the sensor."""
|
|
||||||
if self._units in ("C", "F"):
|
|
||||||
return round(self._state, 1)
|
|
||||||
if isinstance(self._state, float):
|
|
||||||
return round(self._state, 2)
|
|
||||||
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this device."""
|
|
||||||
if self._units in ["C", "F"]:
|
|
||||||
return SensorDeviceClass.TEMPERATURE
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_unit_of_measurement(self):
|
|
||||||
"""Return the unit the value is expressed in."""
|
|
||||||
if self._units == "C":
|
|
||||||
return TEMP_CELSIUS
|
|
||||||
if self._units == "F":
|
|
||||||
return TEMP_FAHRENHEIT
|
|
||||||
return self._units
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveAlarmSensor(ZWaveSensor):
|
|
||||||
"""Representation of a Z-Wave sensor that sends Alarm alerts.
|
|
||||||
|
|
||||||
Examples include certain Multisensors that have motion and vibration
|
|
||||||
capabilities. Z-Wave defines various alarm types such as Smoke, Flood,
|
|
||||||
Burglar, CarbonMonoxide, etc.
|
|
||||||
|
|
||||||
This wraps these alarms and allows you to use them to trigger things, etc.
|
|
||||||
|
|
||||||
COMMAND_CLASS_ALARM is what we get here.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveBatterySensor(ZWaveSensor):
|
|
||||||
"""Representation of Z-Wave device battery level."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this device."""
|
|
||||||
return SensorDeviceClass.BATTERY
|
|
|
@ -1,411 +0,0 @@
|
||||||
# Describes the format for available Z-Wave services
|
|
||||||
|
|
||||||
change_association:
|
|
||||||
name: Change association
|
|
||||||
description: Change an association in the Z-Wave network.
|
|
||||||
fields:
|
|
||||||
association:
|
|
||||||
name: Association
|
|
||||||
description: Specify add or remove association
|
|
||||||
required: true
|
|
||||||
example: add
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the node to set association for.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
target_node_id:
|
|
||||||
name: Target node ID
|
|
||||||
description: Node id of the node to associate to.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
group:
|
|
||||||
name: Group
|
|
||||||
description: Group number to set association for.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 5
|
|
||||||
instance:
|
|
||||||
name: Instance
|
|
||||||
description: Instance of multichannel association.
|
|
||||||
default: 0
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 0
|
|
||||||
max: 255
|
|
||||||
|
|
||||||
add_node:
|
|
||||||
name: Add node
|
|
||||||
description: Add a new (unsecure) node to the Z-Wave network. Refer to OZW_Log.txt for progress.
|
|
||||||
|
|
||||||
add_node_secure:
|
|
||||||
name: Add node secure
|
|
||||||
description: Add a new node to the Z-Wave network with secure communications. Secure network key must be set, this process will fallback to add_node (unsecure) for unsupported devices. Note that unsecure devices can't directly talk to secure devices. Refer to OZW_Log.txt for progress.
|
|
||||||
|
|
||||||
cancel_command:
|
|
||||||
name: Cancel command
|
|
||||||
description: Cancel a running Z-Wave controller command. Use this to exit add_node, if you weren't going to use it but activated it.
|
|
||||||
|
|
||||||
heal_network:
|
|
||||||
name: Heal network
|
|
||||||
description: Start a Z-Wave network heal. This might take a while and will slow down the Z-Wave network greatly while it is being processed. Refer to OZW_Log.txt for progress.
|
|
||||||
fields:
|
|
||||||
return_routes:
|
|
||||||
name: Return routes
|
|
||||||
description: Whether or not to update the return routes from the nodes to the controller.
|
|
||||||
default: false
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
heal_node:
|
|
||||||
name: Heal node
|
|
||||||
description: Start a Z-Wave node heal. Refer to OZW_Log.txt for progress.
|
|
||||||
fields:
|
|
||||||
return_routes:
|
|
||||||
name: Return routes
|
|
||||||
description: Whether or not to update the return routes from the node to the controller.
|
|
||||||
default: false
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
remove_node:
|
|
||||||
name: Remove node
|
|
||||||
description: Remove a node from the Z-Wave network. Refer to OZW_Log.txt for progress.
|
|
||||||
|
|
||||||
remove_failed_node:
|
|
||||||
name: Remove failed node
|
|
||||||
description: This command will remove a failed node from the network. The node should be on the controller's failed nodes list, otherwise this command will fail. Refer to OZW_Log.txt for progress.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to remove.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
|
|
||||||
replace_failed_node:
|
|
||||||
name: Replace failed node
|
|
||||||
description: Replace a failed node with another. If the node is not in the controller's failed nodes list, or the node responds, this command will fail. Refer to OZW_Log.txt for progress.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to replace.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
|
|
||||||
set_config_parameter:
|
|
||||||
name: Set config parameter
|
|
||||||
description: Set a config parameter to a node on the Z-Wave network.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to set config parameter to.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
parameter:
|
|
||||||
name: Parameter
|
|
||||||
description: Parameter number to set.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
value:
|
|
||||||
name: Value
|
|
||||||
description: Value to set for parameter. (String value for list and bool parameters, integer for others).
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
size:
|
|
||||||
name: Size
|
|
||||||
description: Set the size of the parameter value. Only needed if no parameters are available.
|
|
||||||
default: 2
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
|
|
||||||
set_node_value:
|
|
||||||
name: Set node value
|
|
||||||
description: Set the value for a given value_id on a Z-Wave device.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to set the value on.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
value_id:
|
|
||||||
name: Value ID
|
|
||||||
description: Value id of the value to set (integer or string).
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
value:
|
|
||||||
name: Value
|
|
||||||
description: Value to set (integer or string).
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
|
|
||||||
refresh_node_value:
|
|
||||||
name: Refresh node value
|
|
||||||
description: Refresh the value for a given value_id on a Z-Wave device.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to refresh value from.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
value_id:
|
|
||||||
name: Value ID
|
|
||||||
description: Value id of the value to refresh.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
|
|
||||||
set_poll_intensity:
|
|
||||||
name: Set poll intensity
|
|
||||||
description: Set the polling interval to a nodes value
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: ID of the node to set polling to.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
value_id:
|
|
||||||
name: Value ID
|
|
||||||
description: ID of the value to set polling to.
|
|
||||||
example: 72037594255792737
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
poll_intensity:
|
|
||||||
name: Poll intensity
|
|
||||||
description: The intensity to poll, 0 = disabled, 1 = Every time through list, 2 = Every second time through list...
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 0
|
|
||||||
max: 100
|
|
||||||
|
|
||||||
print_config_parameter:
|
|
||||||
name: Print configuration parameter
|
|
||||||
description: Prints a Z-Wave node config parameter value to log.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to print the parameter from.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
parameter:
|
|
||||||
name: Parameter
|
|
||||||
description: Parameter number to print.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
|
|
||||||
print_node:
|
|
||||||
name: Print node
|
|
||||||
description: Print all information about z-wave node.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to print.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
|
|
||||||
refresh_entity:
|
|
||||||
name: Refresh entity
|
|
||||||
description: Refresh zwave entity.
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
name: Entity
|
|
||||||
description: Name of the entity to refresh.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
entity:
|
|
||||||
integration: zwave
|
|
||||||
|
|
||||||
refresh_node:
|
|
||||||
name: Refresh node
|
|
||||||
description: Refresh zwave node.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: ID of the node to refresh.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
|
|
||||||
set_wakeup:
|
|
||||||
name: Set wakeup
|
|
||||||
description: Sets wake-up interval of a node.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to set the wake-up interval for.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
value:
|
|
||||||
name: Value
|
|
||||||
description: Value of the interval to set.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
|
|
||||||
start_network:
|
|
||||||
name: Start network
|
|
||||||
description: Start the Z-Wave network. This might take a while, depending on how big your Z-Wave network is.
|
|
||||||
|
|
||||||
stop_network:
|
|
||||||
name: Stop network
|
|
||||||
description: Stop the Z-Wave network, all updates into Home Assistant will stop.
|
|
||||||
|
|
||||||
soft_reset:
|
|
||||||
name: Soft reset
|
|
||||||
description: This will reset the controller without removing its data. Use carefully because not all controllers support this. Refer to your controller's manual.
|
|
||||||
|
|
||||||
test_network:
|
|
||||||
name: Test network
|
|
||||||
description: This will send test to nodes in the Z-Wave network. This will greatly slow down the Z-Wave network while it is being processed. Refer to OZW_Log.txt for progress.
|
|
||||||
|
|
||||||
test_node:
|
|
||||||
name: Test node
|
|
||||||
description: This will send test messages to a node in the Z-Wave network. This could bring back dead nodes.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: ID of the node to send test messages to.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
messages:
|
|
||||||
name: Messages
|
|
||||||
description: Amount of test messages to send.
|
|
||||||
default: 1
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 100
|
|
||||||
|
|
||||||
rename_node:
|
|
||||||
name: Rename node
|
|
||||||
description: Set the name of a node. This will also affect the IDs of all entities in the node.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: ID of the node to rename.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
update_ids:
|
|
||||||
name: Update IDs
|
|
||||||
description: Rename the entity IDs for entities of this node.
|
|
||||||
default: false
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
name:
|
|
||||||
name: Name
|
|
||||||
description: New Name
|
|
||||||
required: true
|
|
||||||
example: "kitchen"
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
|
|
||||||
rename_value:
|
|
||||||
name: Rename value
|
|
||||||
description: Set the name of a node value. This will affect the ID of the value entity. Value IDs can be queried from /api/zwave/values/{node_id}
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: ID of the node to rename.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
value_id:
|
|
||||||
name: Value ID
|
|
||||||
description: ID of the value to rename.
|
|
||||||
example: 72037594255792737
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
update_ids:
|
|
||||||
name: Update IDs
|
|
||||||
description: Update the entity ID for this value's entity.
|
|
||||||
default: false
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
name:
|
|
||||||
name: Name
|
|
||||||
description: New Name
|
|
||||||
example: "Luminosity"
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
|
|
||||||
reset_node_meters:
|
|
||||||
name: Reset node meters
|
|
||||||
description: Resets the meter counters of a node.
|
|
||||||
fields:
|
|
||||||
node_id:
|
|
||||||
name: Node ID
|
|
||||||
description: Node id of the device to reset meters for.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 255
|
|
||||||
instance:
|
|
||||||
name: Instance
|
|
||||||
description: Instance of association.
|
|
||||||
default: 1
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 1
|
|
||||||
max: 100
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"description": "This integration is no longer maintained. For new installations, use Z-Wave JS instead.\n\nSee https://www.home-assistant.io/docs/z-wave/installation/ for information on the configuration variables",
|
|
||||||
"data": {
|
|
||||||
"usb_path": "[%key:common::config_flow::data::usb_path%]",
|
|
||||||
"network_key": "Network Key (leave blank to auto-generate)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave validation failed. Is the path to the USB stick correct?"
|
|
||||||
},
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
|
||||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"query_stage": {
|
|
||||||
"initializing": "[%key:component::zwave::state::_::initializing%]",
|
|
||||||
"dead": "[%key:component::zwave::state::_::dead%]"
|
|
||||||
},
|
|
||||||
"_": {
|
|
||||||
"initializing": "Initializing",
|
|
||||||
"dead": "Dead",
|
|
||||||
"sleeping": "Sleeping",
|
|
||||||
"ready": "Ready"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
"""Support for Z-Wave switches."""
|
|
||||||
import time
|
|
||||||
|
|
||||||
from homeassistant.components.switch import DOMAIN, SwitchEntity
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
|
|
||||||
from . import ZWaveDeviceEntity, workaround
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Set up Z-Wave Switch from Config Entry."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_add_switch(switch):
|
|
||||||
"""Add Z-Wave Switch."""
|
|
||||||
async_add_entities([switch])
|
|
||||||
|
|
||||||
async_dispatcher_connect(hass, "zwave_new_switch", async_add_switch)
|
|
||||||
|
|
||||||
|
|
||||||
def get_device(values, **kwargs):
|
|
||||||
"""Create zwave entity device."""
|
|
||||||
return ZwaveSwitch(values)
|
|
||||||
|
|
||||||
|
|
||||||
class ZwaveSwitch(ZWaveDeviceEntity, SwitchEntity):
|
|
||||||
"""Representation of a Z-Wave switch."""
|
|
||||||
|
|
||||||
def __init__(self, values):
|
|
||||||
"""Initialize the Z-Wave switch device."""
|
|
||||||
ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
|
||||||
self.refresh_on_update = (
|
|
||||||
workaround.get_device_mapping(values.primary)
|
|
||||||
== workaround.WORKAROUND_REFRESH_NODE_ON_UPDATE
|
|
||||||
)
|
|
||||||
self.last_update = time.perf_counter()
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
|
|
||||||
def update_properties(self):
|
|
||||||
"""Handle data changes for node values."""
|
|
||||||
self._state = self.values.primary.data
|
|
||||||
if self.refresh_on_update and time.perf_counter() - self.last_update > 30:
|
|
||||||
self.last_update = time.perf_counter()
|
|
||||||
self.node.request_state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if device is on."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
|
||||||
"""Turn the device on."""
|
|
||||||
self.node.set_switch(self.values.primary.value_id, True)
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
|
||||||
"""Turn the device off."""
|
|
||||||
self.node.set_switch(self.values.primary.value_id, False)
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Dood",
|
|
||||||
"initializing": "Inisialiseer",
|
|
||||||
"ready": "Gereed",
|
|
||||||
"sleeping": "Aan die slaap"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Dood ({query_stage})",
|
|
||||||
"initializing": "Inisialiseer ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u0645\u0641\u0635\u0648\u0644",
|
|
||||||
"initializing": "\u0642\u064a\u062f \u0627\u0644\u0625\u0646\u0634\u0627\u0621",
|
|
||||||
"ready": "\u062c\u0627\u0647\u0632",
|
|
||||||
"sleeping": "\u0646\u0627\u0626\u0645"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u0645\u0641\u0635\u0648\u0644 ({query_stage})",
|
|
||||||
"initializing": "\u0642\u064a\u062f \u0627\u0644\u0625\u0646\u0634\u0627\u0621 ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "\u0412\u0430\u043b\u0438\u0434\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Z-Wave \u043d\u0435 \u0431\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e. \u041f\u0440\u0430\u0432\u0438\u043b\u0435\u043d \u043b\u0438 \u0435 \u043f\u044a\u0442\u044f\u0442 \u043a\u044a\u043c USB \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\u041c\u0440\u0435\u0436\u043e\u0432 \u043a\u043b\u044e\u0447 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e \u0437\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d\u0435)",
|
|
||||||
"usb_path": "USB \u043f\u044a\u0442"
|
|
||||||
},
|
|
||||||
"description": "\u0412\u0438\u0436\u0442\u0435 https://www.home-assistant.io/docs/z-wave/installation/ \u0437\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e\u0442\u043d\u043e\u0441\u043d\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0438\u0442\u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u043b\u0438\u0432\u0438"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u041c\u044a\u0440\u0442\u044a\u0432",
|
|
||||||
"initializing": "\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f",
|
|
||||||
"ready": "\u0413\u043e\u0442\u043e\u0432",
|
|
||||||
"sleeping": "\u0421\u043f\u044f\u0449"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u041c\u044a\u0440\u0442\u044a\u0432 ({query_stage})",
|
|
||||||
"initializing": "\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Mrtav",
|
|
||||||
"initializing": "Inicijalizacija",
|
|
||||||
"ready": "Spreman",
|
|
||||||
"sleeping": "Spava"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Mrtav ({query_stage})",
|
|
||||||
"initializing": "Inicijalizacija ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "El dispositiu ja est\u00e0 configurat",
|
|
||||||
"single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Ha fallat la validaci\u00f3 de Z-Wave. \u00c9s correcta la ruta al port USB on hi ha connectat el dispositiu?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Clau de xarxa (deixa-ho en blanc per generar-la autom\u00e0ticament)",
|
|
||||||
"usb_path": "Ruta del dispositiu USB"
|
|
||||||
},
|
|
||||||
"description": "Aquesta integraci\u00f3 ja no s'actualitzar\u00e0. Utilitza Z-Wave JS per a instal\u00b7lacions noves.\n\nConsulta https://www.home-assistant.io/docs/z-wave/installation/ per a m\u00e9s informaci\u00f3 sobre les variables de configuraci\u00f3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "No disponible",
|
|
||||||
"initializing": "Inicialitzant",
|
|
||||||
"ready": "A punt",
|
|
||||||
"sleeping": "Dormint"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "No disponible",
|
|
||||||
"initializing": "Inicialitzant"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno",
|
|
||||||
"single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave ov\u011b\u0159en\u00ed se nezda\u0159ilo. Je cesta k USB za\u0159\u00edzen\u00ed spr\u00e1vn\u011b?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "S\u00ed\u0165ov\u00fd kl\u00ed\u010d (ponechte pr\u00e1zdn\u00e9 pro automatick\u00e9 generov\u00e1n\u00ed)",
|
|
||||||
"usb_path": "Cesta k USB za\u0159\u00edzen\u00ed"
|
|
||||||
},
|
|
||||||
"description": "Viz https://www.home-assistant.io/docs/z-wave/installation/ pro informace o konfigura\u010dn\u00edch prom\u011bnn\u00fdch"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Nereaguje",
|
|
||||||
"initializing": "Inicializace",
|
|
||||||
"ready": "P\u0159ipraveno",
|
|
||||||
"sleeping": "\u00dasporn\u00fd re\u017eim"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Nereaguje",
|
|
||||||
"initializing": "Inicializace"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Marw",
|
|
||||||
"initializing": "Ymgychwyn",
|
|
||||||
"ready": "Barod",
|
|
||||||
"sleeping": "Cysgu"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Marw ({query_stage})",
|
|
||||||
"initializing": "Ymgychwyn ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave er allerede konfigureret"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave-validering mislykkedes. Er stien til USB-enhed korrekt?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Netv\u00e6rksn\u00f8gle (efterlad blank for autogenerering)",
|
|
||||||
"usb_path": "Sti til USB-enhed"
|
|
||||||
},
|
|
||||||
"description": "Se https://www.home-assistant.io/docs/z-wave/installation/ for oplysninger om konfigurationsvariabler"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "D\u00f8d",
|
|
||||||
"initializing": "Initialiserer",
|
|
||||||
"ready": "Klar",
|
|
||||||
"sleeping": "Sover"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "D\u00f8d ({query_stage})",
|
|
||||||
"initializing": "Initialiserer ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Ger\u00e4t ist bereits konfiguriert",
|
|
||||||
"single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave-Validierung fehlgeschlagen. Ist der Pfad zum USB-Stick korrekt?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Netzwerkschl\u00fcssel (leer lassen, um automatisch zu generieren)",
|
|
||||||
"usb_path": "USB-Ger\u00e4te-Pfad"
|
|
||||||
},
|
|
||||||
"description": "Diese Integration wird nicht mehr gepflegt. Verwende bei Neuinstallationen stattdessen Z-Wave JS.\n\nSiehe https://www.home-assistant.io/docs/z-wave/installation/ f\u00fcr Informationen zu den Konfigurationsvariablen"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Nicht erreichbar",
|
|
||||||
"initializing": "Initialisierend",
|
|
||||||
"ready": "Bereit",
|
|
||||||
"sleeping": "Schlafend"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Nicht erreichbar ({query_stage})",
|
|
||||||
"initializing": "Initialisierend"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af",
|
|
||||||
"single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "\u0397 \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 Z-Wave \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5. \u0395\u03af\u03bd\u03b1\u03b9 \u03c3\u03c9\u03c3\u03c4\u03ae \u03b7 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03c3\u03c4\u03b9\u03ba\u03ac\u03ba\u03b9 USB;"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1)",
|
|
||||||
"usb_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 USB"
|
|
||||||
},
|
|
||||||
"description": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b4\u03b5\u03bd \u03b4\u03b9\u03b1\u03c4\u03b7\u03c1\u03b5\u03af\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd. \u0393\u03b9\u03b1 \u03bd\u03ad\u03b5\u03c2 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Z-Wave JS. \n\n \u0394\u03b5\u03af\u03c4\u03b5 https://www.home-assistant.io/docs/z-wave/installation/ \u03b3\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03c4\u03b9\u03c2 \u03bc\u03b5\u03c4\u03b1\u03b2\u03bb\u03b7\u03c4\u03ad\u03c2 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u039d\u03b5\u03ba\u03c1\u03cc",
|
|
||||||
"initializing": "\u0391\u03c1\u03c7\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7",
|
|
||||||
"ready": "\u0388\u03c4\u03bf\u03b9\u03bc\u03bf",
|
|
||||||
"sleeping": "\u039a\u03bf\u03b9\u03bc\u03ac\u03c4\u03b1\u03b9"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u039d\u03b5\u03ba\u03c1\u03cc",
|
|
||||||
"initializing": "\u0391\u03c1\u03c7\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Device is already configured",
|
|
||||||
"single_instance_allowed": "Already configured. Only a single configuration possible."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave validation failed. Is the path to the USB stick correct?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Network Key (leave blank to auto-generate)",
|
|
||||||
"usb_path": "USB Device Path"
|
|
||||||
},
|
|
||||||
"description": "This integration is no longer maintained. For new installations, use Z-Wave JS instead.\n\nSee https://www.home-assistant.io/docs/z-wave/installation/ for information on the configuration variables"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Dead",
|
|
||||||
"initializing": "Initializing",
|
|
||||||
"ready": "Ready",
|
|
||||||
"sleeping": "Sleeping"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Dead",
|
|
||||||
"initializing": "Initializing"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave ya est\u00e1 configurado"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "La validaci\u00f3n de Z-Wave fall\u00f3. \u00bfEs correcta la ruta a la memoria USB?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Clave de red (dejar en blanco para auto-generar)",
|
|
||||||
"usb_path": "Ruta USB"
|
|
||||||
},
|
|
||||||
"description": "Consulte https://www.home-assistant.io/docs/z-wave/installation/ para obtener informaci\u00f3n sobre las variables de configuraci\u00f3n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Desconectado",
|
|
||||||
"initializing": "Iniciando",
|
|
||||||
"ready": "Listo",
|
|
||||||
"sleeping": "Hibernacion"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Desconectado ({query_stage})",
|
|
||||||
"initializing": "Iniciando ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave ya est\u00e1 configurado",
|
|
||||||
"single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave error de validaci\u00f3n. \u00bfLa ruta de acceso a la memoria USB escorrecta?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Clave de red (d\u00e9jelo en blanco para generar autom\u00e1ticamente)",
|
|
||||||
"usb_path": "Ruta del dispositivo USB"
|
|
||||||
},
|
|
||||||
"description": "Consulta https://www.home-assistant.io/docs/z-wave/installation/ para obtener informaci\u00f3n sobre las variables de configuraci\u00f3n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "No responde",
|
|
||||||
"initializing": "Inicializando",
|
|
||||||
"ready": "Listo",
|
|
||||||
"sleeping": "Ahorro de energ\u00eda"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "No responde",
|
|
||||||
"initializing": "Inicializando"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Seade on juba h\u00e4\u00e4lestatud",
|
|
||||||
"single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave valideerimine nurjus. Kas USB-m\u00e4lupulga tee on \u00f5ige?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "V\u00f5rguv\u00f5ti (j\u00e4ta automaatse genereerimise jaoks t\u00fchjaks)",
|
|
||||||
"usb_path": "USB seadme rada"
|
|
||||||
},
|
|
||||||
"description": "Seda sidumist enam ei hallata. Uueks sidumiseks kasuta Z-Wave JS.\n\nKonfiguratsioonimuutujate kohta leiad teavet https://www.home-assistant.io/docs/z-wave/installation/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Surnud",
|
|
||||||
"initializing": "L\u00e4htestan",
|
|
||||||
"ready": "Valmis",
|
|
||||||
"sleeping": "Ootel"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Surnud",
|
|
||||||
"initializing": "L\u00e4htestan"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Hilda",
|
|
||||||
"initializing": "Hasieratzen",
|
|
||||||
"ready": "Prest",
|
|
||||||
"sleeping": "Lotan"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Ez du erantzuten ({query_stage})",
|
|
||||||
"initializing": "Hasieratzen ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u0645\u0631\u062f\u0647",
|
|
||||||
"initializing": "\u062f\u0631 \u062d\u0627\u0644 \u0622\u0645\u0627\u062f\u0647 \u0634\u062f\u0646",
|
|
||||||
"ready": "\u0622\u0645\u0627\u062f\u0647",
|
|
||||||
"sleeping": "\u062f\u0631 \u062d\u0627\u0644 \u062e\u0648\u0627\u0628"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u0645\u0631\u062f\u0647 ({query_stage})",
|
|
||||||
"initializing": "\u062f\u0631 \u062d\u0627\u0644 \u0622\u0645\u0627\u062f\u0647 \u0634\u062f\u0646 ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"usb_path": "USB-polku"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Kuollut",
|
|
||||||
"initializing": "Alustaa",
|
|
||||||
"ready": "Valmis",
|
|
||||||
"sleeping": "Lepotilassa"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Kuollut ({query_stage})",
|
|
||||||
"initializing": "Alustaa ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
|
||||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "La validation Z-Wave a \u00e9chou\u00e9. Le chemin d'acc\u00e8s \u00e0 la cl\u00e9 USB est-il correct?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Cl\u00e9 r\u00e9seau (laisser vide pour g\u00e9n\u00e9rer automatiquement)",
|
|
||||||
"usb_path": "Chemin du p\u00e9riph\u00e9rique USB"
|
|
||||||
},
|
|
||||||
"description": "Voir https://www.home-assistant.io/docs/z-wave/installation/ pour plus d'informations sur les variables de configuration."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Morte",
|
|
||||||
"initializing": "Initialisation",
|
|
||||||
"ready": "Pr\u00eat",
|
|
||||||
"sleeping": "En veille"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Morte",
|
|
||||||
"initializing": "Initialisation"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Tod",
|
|
||||||
"initializing": "Inizialisi\u00e4r\u00e4",
|
|
||||||
"ready": "Parat",
|
|
||||||
"sleeping": "Schlaf\u00e4"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Tod ({query_stage})",
|
|
||||||
"initializing": "Inizialisi\u00e4r\u00e4 ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4",
|
|
||||||
"single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea."
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"usb_path": "\u05e0\u05ea\u05d9\u05d1 \u05d4\u05ea\u05e7\u05df USB"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u05de\u05ea",
|
|
||||||
"initializing": "\u05d0\u05ea\u05d7\u05d5\u05dc",
|
|
||||||
"ready": "\u05de\u05d5\u05db\u05df",
|
|
||||||
"sleeping": "\u05d9\u05e9\u05df"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u05de\u05ea",
|
|
||||||
"initializing": "\u05d0\u05ea\u05d7\u05d5\u05dc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u092e\u0943\u0924",
|
|
||||||
"initializing": "\u0906\u0930\u0902\u092d",
|
|
||||||
"ready": "\u0924\u0948\u092f\u093e\u0930",
|
|
||||||
"sleeping": "\u0938\u094b\u092f\u093e \u0939\u0941\u0906"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": " ( {query_stage} )",
|
|
||||||
"initializing": "\u0906\u0930\u0902\u092d ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Mrtav",
|
|
||||||
"initializing": "Inicijalizacija",
|
|
||||||
"ready": "Spreman",
|
|
||||||
"sleeping": "Spavanje"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Mrtav ({query_stage})",
|
|
||||||
"initializing": "Inicijalizacija ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van",
|
|
||||||
"single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "A Z-Wave \u00e9rv\u00e9nyes\u00edt\u00e9s sikertelen. Az USB-meghajt\u00f3 el\u00e9r\u00e9si \u00fatj\u00e1t helyesen adtad meg?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "H\u00e1l\u00f3zati kulcs (hagyja \u00fcresen az automatikus gener\u00e1l\u00e1shoz)",
|
|
||||||
"usb_path": "USB eszk\u00f6z el\u00e9r\u00e9si \u00fat"
|
|
||||||
},
|
|
||||||
"description": "Ezt az integr\u00e1ci\u00f3t m\u00e1r nem tartj\u00e1k fenn. \u00daj telep\u00edt\u00e9sek eset\u00e9n haszn\u00e1lja helyette a Z-Wave JS-t.\n\nA konfigur\u00e1ci\u00f3s v\u00e1ltoz\u00f3kkal kapcsolatos inform\u00e1ci\u00f3k\u00e9rt l\u00e1sd https://www.home-assistant.io/docs/z-wave/installation/."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Nem ad \u00e9letjelet",
|
|
||||||
"initializing": "Inicializ\u00e1l\u00e1s",
|
|
||||||
"ready": "K\u00e9sz",
|
|
||||||
"sleeping": "Alv\u00e1s"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Nem ad \u00e9letjelet",
|
|
||||||
"initializing": "Inicializ\u00e1l\u00e1s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u0544\u0565\u057c\u0561\u056e",
|
|
||||||
"initializing": "\u0546\u0561\u056d\u0561\u0571\u0565\u057c\u0576\u0578\u0572",
|
|
||||||
"ready": "\u054a\u0561\u057f\u0580\u0561\u057d\u057f \u0567",
|
|
||||||
"sleeping": "\u0554\u0576\u0565\u056c"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u0544\u0561\u0570\u0561\u0581\u0561\u056e{query_stage})",
|
|
||||||
"initializing": "\u0546\u0561\u056d\u0561\u0571\u0565\u057c\u0576\u0578\u0582\u0569\u0575\u0578\u0582\u0576({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Perangkat sudah dikonfigurasi",
|
|
||||||
"single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Validasi Z-Wave gagal. Apakah jalur ke stik USB sudah benar?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Kunci Jaringan (biarkan kosong untuk dibuat secara otomatis)",
|
|
||||||
"usb_path": "Jalur Perangkat USB"
|
|
||||||
},
|
|
||||||
"description": "Integrasi ini tidak lagi dipertahankan. Untuk instalasi baru, gunakan Z-Wave JS sebagai gantinya.\n\nBaca https://www.home-assistant.io/docs/z-wave/installation/ untuk informasi tentang variabel konfigurasi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Mati",
|
|
||||||
"initializing": "Inisialisasi",
|
|
||||||
"ready": "Siap",
|
|
||||||
"sleeping": "Tidur"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Mati",
|
|
||||||
"initializing": "Inisialisasi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Dau\u00f0ur",
|
|
||||||
"initializing": "Frumstilli",
|
|
||||||
"ready": "Tilb\u00fainn",
|
|
||||||
"sleeping": "\u00cd dvala"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Dau\u00f0ur ({query_stage})",
|
|
||||||
"initializing": "Frumstilli ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato",
|
|
||||||
"single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Convalida Z-Wave non riuscita. Il percorso della chiavetta USB \u00e8 corretto?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Chiave di rete (lascia vuoto per generare automaticamente)",
|
|
||||||
"usb_path": "Percorso del dispositivo USB"
|
|
||||||
},
|
|
||||||
"description": "Questa integrazione non viene pi\u00f9 mantenuta. Per le nuove installazioni, usa invece Z-Wave JS. \n\nVedere https://www.home-assistant.io/docs/z-wave/installation/ per informazioni sulle variabili di configurazione"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Disattivo",
|
|
||||||
"initializing": "In avvio",
|
|
||||||
"ready": "Pronto",
|
|
||||||
"sleeping": "Dormiente"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Disattivo",
|
|
||||||
"initializing": "In avvio"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059",
|
|
||||||
"single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave\u306e\u691c\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002USB\u30b9\u30c6\u30a3\u30c3\u30af\u3078\u306e\u30d1\u30b9\u306f\u6b63\u3057\u3044\u3067\u3059\u304b\uff1f"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30ad\u30fc(\u7a7a\u767d\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)",
|
|
||||||
"usb_path": "USB\u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9"
|
|
||||||
},
|
|
||||||
"description": "\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30e1\u30f3\u30c6\u30ca\u30f3\u30b9\u306f\u7d42\u4e86\u3057\u307e\u3057\u305f\u3002\u65b0\u898f\u306b\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u5834\u5408\u306f\u3001\u4ee3\u308f\u308a\u306bZ-Wave JS\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n\u69cb\u6210\u5909\u6570\u306e\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001https://www.home-assistant.io/docs/z-wave/installation/ \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u30c7\u30c3\u30c9",
|
|
||||||
"initializing": "\u521d\u671f\u5316\u4e2d",
|
|
||||||
"ready": "\u6e96\u5099\u5b8c\u4e86",
|
|
||||||
"sleeping": "\u30b9\u30ea\u30fc\u30d7"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u30c7\u30c3\u30c9",
|
|
||||||
"initializing": "\u521d\u671f\u5316\u4e2d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4",
|
|
||||||
"single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave \uc720\ud6a8\uc131 \uac80\uc0ac\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. USB \uc2a4\ud2f1\uc758 \uacbd\ub85c\uac00 \uc815\ud655\ud569\ub2c8\uae4c?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\ub124\ud2b8\uc6cc\ud06c \ud0a4 (\uacf5\ub780\uc73c\ub85c \ube44\uc6cc\ub450\uba74 \uc790\ub3d9 \uc0dd\uc131\ud569\ub2c8\ub2e4)",
|
|
||||||
"usb_path": "USB \uc7a5\uce58 \uacbd\ub85c"
|
|
||||||
},
|
|
||||||
"description": "\uc774 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub294 \ub354 \uc774\uc0c1 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uc0c8\ub85c\uc6b4 \uc124\uce58\uc758 \uacbd\uc6b0 Z-Wave JS \ub97c \uc0ac\uc6a9\ud574\uc8fc\uc138\uc694.\n\n\uad6c\uc131 \ubcc0\uc218\uc5d0 \ub300\ud55c \uc815\ubcf4\ub294 https://www.home-assistant.io/docs/z-wave/installation/ \uc744 \ucc38\uc870\ud574\uc8fc\uc138\uc694"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\uc751\ub2f5\uc5c6\uc74c",
|
|
||||||
"initializing": "\ucd08\uae30\ud654\uc911",
|
|
||||||
"ready": "\uc900\ube44",
|
|
||||||
"sleeping": "\uc808\uc804\ubaa8\ub4dc"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\uc751\ub2f5\uc5c6\uc74c",
|
|
||||||
"initializing": "\ucd08\uae30\ud654\uc911"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Apparat ass scho konfigur\u00e9iert",
|
|
||||||
"single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun m\u00e9iglech."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave Validatioun net g\u00eblteg. Ass de Pad zum USB Stick richteg?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Netzwierk Schl\u00ebssel (eidel loossen fir een automatesch z'erstellen)",
|
|
||||||
"usb_path": "Pad zum USB Apparat"
|
|
||||||
},
|
|
||||||
"description": "Lies op https://www.home-assistant.io/docs/z-wave/installation/ fir weider Informatiounen iwwert d'Konfiguratioun vun den Variabelen"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Doud",
|
|
||||||
"initializing": "Initialis\u00e9iert",
|
|
||||||
"ready": "Bereet",
|
|
||||||
"sleeping": "Schl\u00e9ift"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Doud",
|
|
||||||
"initializing": "Initialis\u00e9iert"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"query_stage": {
|
|
||||||
"dead": " ({query_stage})",
|
|
||||||
"initializing": " ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Beigta",
|
|
||||||
"initializing": "Inicializ\u0113",
|
|
||||||
"ready": "Gatavs",
|
|
||||||
"sleeping": "Gu\u013c"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Beigta ({query_stage})",
|
|
||||||
"initializing": "Inicializ\u0113 ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "D\u00f8d",
|
|
||||||
"initializing": "Initialiserer",
|
|
||||||
"ready": "Klar",
|
|
||||||
"sleeping": "Sover"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "D\u00f8d ({query_stage})",
|
|
||||||
"initializing": "Initialiserer ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Apparaat is al geconfigureerd",
|
|
||||||
"single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave-validatie mislukt. Is het pad naar de USB-stick correct?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Netwerksleutel (laat leeg om automatisch te genereren)",
|
|
||||||
"usb_path": "USB-apparaatpad"
|
|
||||||
},
|
|
||||||
"description": "Deze integratie wordt niet langer onderhouden. Voor nieuwe installaties, gebruik Z-Wave JS in plaats daarvan.\n\nZie https://www.home-assistant.io/docs/z-wave/installation/ voor informatie over de configuratievariabelen"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Onbereikbaar",
|
|
||||||
"initializing": "Initialiseren",
|
|
||||||
"ready": "Gereed",
|
|
||||||
"sleeping": "Slaapt"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Onbereikbaar",
|
|
||||||
"initializing": "Initialiseren"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"description": "Sj\u00e5 [www.home-assistant.io/docs/z-wave/installation/](https://www.home-assistant.io/docs/z-wave/installation/) for informasjon om konfigurasjonsvariablene."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "D\u00f8d",
|
|
||||||
"initializing": "Initialiserer",
|
|
||||||
"ready": "Klar",
|
|
||||||
"sleeping": "S\u00f8v"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "D\u00f8d ({query_stage})",
|
|
||||||
"initializing": "Initialiserer ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Enheten er allerede konfigurert",
|
|
||||||
"single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave-validering mislyktes. Er banen til USB dongel riktig?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Nettverksn\u00f8kkel (la v\u00e6re tom for automatisk oppretting)",
|
|
||||||
"usb_path": "USB enhetsbane"
|
|
||||||
},
|
|
||||||
"description": "Denne integrasjonen opprettholdes ikke lenger. For nye installasjoner, bruk Z-Wave JS i stedet. \n\n Se https://www.home-assistant.io/docs/z-wave/installation/ for informasjon om konfigurasjonsvariablene"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "D\u00f8d",
|
|
||||||
"initializing": "Initialiserer",
|
|
||||||
"ready": "Klar",
|
|
||||||
"sleeping": "Sover"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "D\u00f8d",
|
|
||||||
"initializing": "Initialiserer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane",
|
|
||||||
"single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Walidacja Z-Wave si\u0119 nie powiod\u0142a. Czy \u015bcie\u017cka do kontrolera Z-Wave USB jest prawid\u0142owa?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Klucz sieciowy (pozostaw pusty, by generowa\u0107 automatycznie)",
|
|
||||||
"usb_path": "\u015acie\u017cka urz\u0105dzenia USB"
|
|
||||||
},
|
|
||||||
"description": "Ta integracja nie jest ju\u017c wspierana. Dla nowych instalacji, u\u017cyj Z-Wave JS.\n\nPrzejd\u017a na https://www.home-assistant.io/docs/z-wave/installation/, aby uzyska\u0107 informacje na temat zmiennych konfiguracyjnych"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "martwy",
|
|
||||||
"initializing": "inicjalizacja",
|
|
||||||
"ready": "gotowy",
|
|
||||||
"sleeping": "u\u015bpiony"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "martwy",
|
|
||||||
"initializing": "inicjalizacja"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Dispositivo j\u00e1 est\u00e1 configurado",
|
|
||||||
"single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "A valida\u00e7\u00e3o Z-Wave falhou. O caminho para o USB est\u00e1 correto?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Chave de rede (deixe em branco para gerar automaticamente)",
|
|
||||||
"usb_path": "Caminho do Dispositivo USB"
|
|
||||||
},
|
|
||||||
"description": "Consulte https://www.home-assistant.io/docs/z-wave/installation/ para obter informa\u00e7\u00f5es sobre as vari\u00e1veis de configura\u00e7\u00e3o"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Morto",
|
|
||||||
"initializing": "Iniciando",
|
|
||||||
"ready": "Pronto",
|
|
||||||
"sleeping": "Dormindo"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Morto",
|
|
||||||
"initializing": "Iniciando"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "O Z-Wave j\u00e1 est\u00e1 configurado",
|
|
||||||
"single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "A valida\u00e7\u00e3o Z-Wave falhou. O caminho para o dispositivo USB est\u00e1 correto?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Network Key (deixe em branco para auto-gera\u00e7\u00e3o)",
|
|
||||||
"usb_path": "Endere\u00e7o USB"
|
|
||||||
},
|
|
||||||
"description": "Consulte https://www.home-assistant.io/docs/z-wave/installation/ para obter informa\u00e7\u00f5es sobre as vari\u00e1veis de configura\u00e7\u00e3o"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Morto",
|
|
||||||
"initializing": "A inicializar",
|
|
||||||
"ready": "Pronto",
|
|
||||||
"sleeping": "Adormecido"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Morto ({query_stage})",
|
|
||||||
"initializing": "A inicializar ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave este deja configurat"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Validarea Z-Wave a e\u0219uat. Este corect\u0103 calea c\u0103tre stick-ul USB?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Cheie de re\u021bea (l\u0103sa\u021bi necompletat pentru a genera automat)",
|
|
||||||
"usb_path": "Cale USB"
|
|
||||||
},
|
|
||||||
"description": "Vede\u021bi https://www.home-assistant.io/docs/z-wave/installation/ pentru informa\u021bii despre variabilele de configurare"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Inactiv",
|
|
||||||
"initializing": "Se ini\u021bializeaz\u0103",
|
|
||||||
"ready": "Disponibil",
|
|
||||||
"sleeping": "Adormit"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Inactiv ({query_stage})",
|
|
||||||
"initializing": "Se ini\u021bializeaz\u0103 ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.",
|
|
||||||
"single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 Z-Wave. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443."
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\u041a\u043b\u044e\u0447 \u0441\u0435\u0442\u0438 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438)",
|
|
||||||
"usb_path": "\u041f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443"
|
|
||||||
},
|
|
||||||
"description": "\u042d\u0442\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f. \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0435\u0451 Z-Wave JS.\n\n\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/docs/z-wave/installation/) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e",
|
|
||||||
"initializing": "\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f",
|
|
||||||
"ready": "\u0413\u043e\u0442\u043e\u0432",
|
|
||||||
"sleeping": "\u0420\u0435\u0436\u0438\u043c \u0441\u043d\u0430"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e",
|
|
||||||
"initializing": "\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Nereaguje",
|
|
||||||
"initializing": "Inicializ\u00e1cia",
|
|
||||||
"ready": "Pripraven\u00e9",
|
|
||||||
"sleeping": "\u00dasporn\u00fd re\u017eim"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Nereaguje ({query_stage})",
|
|
||||||
"initializing": "Inicializ\u00e1cia ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave je \u017ee konfiguriran"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Potrjevanje Z-Wave ni uspelo. Ali je pot do USB klju\u010da pravilna?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "Omre\u017eni klju\u010d (pustite prazno za samodejno generiranje)",
|
|
||||||
"usb_path": "USB Pot"
|
|
||||||
},
|
|
||||||
"description": "Za informacije o konfiguracijskih spremenljivka si oglejte https://www.home-assistant.io/docs/z-wave/installation/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "Mrtva",
|
|
||||||
"initializing": "Inicializacija",
|
|
||||||
"ready": "Pripravljen",
|
|
||||||
"sleeping": "Spi"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "Mrtva",
|
|
||||||
"initializing": "Inicializacija"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"query_stage": {
|
|
||||||
"dead": " ({query_stage})",
|
|
||||||
"initializing": " ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"ready": "Spreman"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": " ({query_stage})",
|
|
||||||
"initializing": " ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave \u00e4r redan konfigurerat"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave-valideringen misslyckades. \u00c4r s\u00f6kv\u00e4gen till USB-minnet korrekt?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "N\u00e4tverksnyckel (l\u00e4mna blank f\u00f6r automatisk generering)",
|
|
||||||
"usb_path": "USB-s\u00f6kv\u00e4g"
|
|
||||||
},
|
|
||||||
"description": "Se https://www.home-assistant.io/docs/z-wave/installation/ f\u00f6r information om konfigurationsvariabler"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "D\u00f6d",
|
|
||||||
"initializing": "Initierar",
|
|
||||||
"ready": "Redo",
|
|
||||||
"sleeping": "Sovande"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "D\u00f6d ({query_stage})",
|
|
||||||
"initializing": "Initierar ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u0b87\u0bb1\u0ba8\u0bcd\u0ba4\u0bc1\u0bb5\u0bbf\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1",
|
|
||||||
"initializing": "\u0ba4\u0bc1\u0bb5\u0b95\u0bcd\u0b95\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1",
|
|
||||||
"ready": "\u0ba4\u0baf\u0bbe\u0bb0\u0bcd",
|
|
||||||
"sleeping": "\u0ba4\u0bc2\u0b99\u0bcd\u0b95\u0bc1\u0b95\u0bbf\u0ba9\u0bcd\u0bb1\u0ba4\u0bc1"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u0b87\u0bb1\u0ba8\u0bcd\u0ba4\u0bc1\u0bb5\u0bbf\u0b9f\u0bcd\u0b9f\u0ba4\u0bc1 ({query_stage})",
|
|
||||||
"initializing": "\u0ba4\u0bc1\u0bb5\u0b95\u0bcd\u0b95\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1 ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u0c2e\u0c43\u0c24 \u0c2a\u0c30\u0c3f\u0c15\u0c30\u0c02",
|
|
||||||
"initializing": "\u0c38\u0c3f\u0c26\u0c4d\u0c27\u0c02 \u0c05\u0c35\u0c41\u0c24\u0c4b\u0c02\u0c26\u0c3f",
|
|
||||||
"ready": "\u0c30\u0c46\u0c21\u0c40",
|
|
||||||
"sleeping": "\u0c28\u0c3f\u0c26\u0c4d\u0c30\u0c3f\u0c38\u0c4d\u0c24\u0c4b\u0c02\u0c26\u0c3f"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u0c2e\u0c43\u0c24 \u0c2a\u0c30\u0c3f\u0c15\u0c30\u0c02 ({query_stage})",
|
|
||||||
"initializing": "\u0c38\u0c3f\u0c26\u0c4d\u0c27\u0c02 \u0c05\u0c35\u0c41\u0c24\u0c4b\u0c02\u0c26\u0c3f ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u0e44\u0e21\u0e48\u0e1e\u0e23\u0e49\u0e2d\u0e21\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19",
|
|
||||||
"initializing": "\u0e01\u0e33\u0e25\u0e31\u0e07\u0e40\u0e23\u0e34\u0e48\u0e21\u0e15\u0e49\u0e19",
|
|
||||||
"ready": "\u0e1e\u0e23\u0e49\u0e2d\u0e21\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19",
|
|
||||||
"sleeping": "\u0e01\u0e33\u0e25\u0e31\u0e07\u0e2b\u0e25\u0e31\u0e1a"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u0e44\u0e21\u0e48\u0e1e\u0e23\u0e49\u0e2d\u0e21\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19 ({query_stage})",
|
|
||||||
"initializing": "\u0e01\u0e33\u0e25\u0e31\u0e07\u0e40\u0e23\u0e34\u0e48\u0e21\u0e15\u0e49\u0e19 ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f",
|
|
||||||
"single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave do\u011frulamas\u0131 ba\u015far\u0131s\u0131z oldu. USB stickin yolu do\u011fru mu?"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "A\u011f Anajtar\u0131 (otomatik \u00fcretilmesi i\u00e7in bo\u015f b\u0131rak\u0131n\u0131z)",
|
|
||||||
"usb_path": "USB Cihaz Yolu"
|
|
||||||
},
|
|
||||||
"description": "Bu entegrasyon art\u0131k korunmuyor. Yeni kurulumlar i\u00e7in bunun yerine Z-Wave JS kullan\u0131n. \n\n Yap\u0131land\u0131rma de\u011fi\u015fkenleri hakk\u0131nda bilgi i\u00e7in https://www.home-assistant.io/docs/z-wave/installation/ adresine bak\u0131n."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u00d6l\u00fc",
|
|
||||||
"initializing": "Ba\u015flat\u0131l\u0131yor",
|
|
||||||
"ready": "Haz\u0131r",
|
|
||||||
"sleeping": "Uyuyor"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u00d6l\u00fc",
|
|
||||||
"initializing": "Ba\u015flat\u0131l\u0131yor"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "\u0426\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant.",
|
|
||||||
"single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "\u041f\u043e\u043c\u0438\u043b\u043a\u0430 \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0438 Z-Wave. \u041f\u0435\u0440\u0435\u0432\u0456\u0440\u0442\u0435 \u0448\u043b\u044f\u0445 \u0434\u043e USB-\u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e."
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\u041a\u043b\u044e\u0447 \u043c\u0435\u0440\u0435\u0436\u0456 (\u0437\u0430\u043b\u0438\u0448\u0442\u0435 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438)",
|
|
||||||
"usb_path": "\u0428\u043b\u044f\u0445 \u0434\u043e USB-\u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e"
|
|
||||||
},
|
|
||||||
"description": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 [\u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438](https://www.home-assistant.io/docs/z-wave/installation/) \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0431\u0456\u043b\u044c\u0448 \u0434\u043e\u043a\u043b\u0430\u0434\u043d\u043e\u0457 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457 \u043f\u0440\u043e \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u041d\u0435\u0441\u043f\u0440\u0430\u0432\u043d\u0438\u0439",
|
|
||||||
"initializing": "\u0406\u043d\u0456\u0446\u0456\u0430\u043b\u0456\u0437\u0430\u0446\u0456\u044f",
|
|
||||||
"ready": "\u0413\u043e\u0442\u043e\u0432\u0438\u0439",
|
|
||||||
"sleeping": "\u0420\u0435\u0436\u0438\u043c \u0441\u043d\u0443"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u041d\u0435\u0441\u043f\u0440\u0430\u0432\u043d\u0438\u0439",
|
|
||||||
"initializing": "\u0406\u043d\u0456\u0446\u0456\u0430\u043b\u0456\u0437\u0430\u0446\u0456\u044f"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u0110\u00e3 t\u1eaft",
|
|
||||||
"initializing": "Kh\u1edfi t\u1ea1o",
|
|
||||||
"ready": "S\u1eb5n s\u00e0ng",
|
|
||||||
"sleeping": "Ng\u1ee7"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u0110\u00e3 t\u1eaft ({query_stage})",
|
|
||||||
"initializing": "Kh\u1edfi t\u1ea1o ( {query_stage} )"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "Z-Wave \u5df2\u914d\u7f6e\u5b8c\u6210"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave \u9a8c\u8bc1\u5931\u8d25\u3002 USB \u68d2\u7684\u8def\u5f84\u662f\u5426\u6b63\u786e\uff1f"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\u7f51\u7edc\u5bc6\u94a5\uff08\u7559\u7a7a\u5c06\u81ea\u52a8\u751f\u6210\uff09",
|
|
||||||
"usb_path": "USB \u8def\u5f84"
|
|
||||||
},
|
|
||||||
"description": "\u6709\u5173\u914d\u7f6e\u7684\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 https://www.home-assistant.io/docs/z-wave/installation/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u65ad\u5f00",
|
|
||||||
"initializing": "\u521d\u59cb\u5316",
|
|
||||||
"ready": "\u5c31\u7eea",
|
|
||||||
"sleeping": "\u4f11\u7720"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u65ad\u5f00 ({query_stage})",
|
|
||||||
"initializing": "\u521d\u59cb\u5316 ({query_stage})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
|
|
||||||
"single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"option_error": "Z-Wave \u9a57\u8b49\u5931\u6557\uff0c\u8acb\u78ba\u5b9a USB \u96a8\u8eab\u789f\u8def\u5f91\u6b63\u78ba\uff1f"
|
|
||||||
},
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"data": {
|
|
||||||
"network_key": "\u7db2\u8def\u91d1\u9470\uff08\u4fdd\u7559\u7a7a\u767d\u5c07\u6703\u81ea\u52d5\u7522\u751f\uff09",
|
|
||||||
"usb_path": "USB \u88dd\u7f6e\u8def\u5f91"
|
|
||||||
},
|
|
||||||
"description": "\u6b64\u6574\u5408\u5df2\u7d93\u4e0d\u518d\u9032\u884c\u7dad\u8b77\uff0c\u8acb\u4f7f\u7528 Z-Wave JS \u53d6\u4ee3\u70ba\u65b0\u5b89\u88dd\u65b9\u5f0f\u3002\n\n\u8acb\u53c3\u95b1 https://www.home-assistant.io/docs/z-wave/installation/ \u4ee5\n\u7372\u5f97\u8a2d\u5b9a\u8b8a\u6578\u8cc7\u8a0a"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"_": {
|
|
||||||
"dead": "\u5931\u53bb\u9023\u7dda",
|
|
||||||
"initializing": "\u6b63\u5728\u521d\u59cb\u5316",
|
|
||||||
"ready": "\u6e96\u5099\u5c31\u7dd2",
|
|
||||||
"sleeping": "\u4f11\u7720\u4e2d"
|
|
||||||
},
|
|
||||||
"query_stage": {
|
|
||||||
"dead": "\u5931\u53bb\u9023\u7dda",
|
|
||||||
"initializing": "\u6b63\u5728\u521d\u59cb\u5316"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
"""Zwave util methods."""
|
|
||||||
import asyncio
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
from . import const
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def check_node_schema(node, schema):
|
|
||||||
"""Check if node matches the passed node schema."""
|
|
||||||
if const.DISC_NODE_ID in schema and node.node_id not in schema[const.DISC_NODE_ID]:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"node.node_id %s not in node_id %s",
|
|
||||||
node.node_id,
|
|
||||||
schema[const.DISC_NODE_ID],
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if (
|
|
||||||
const.DISC_GENERIC_DEVICE_CLASS in schema
|
|
||||||
and node.generic not in schema[const.DISC_GENERIC_DEVICE_CLASS]
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"node.generic %s not in generic_device_class %s",
|
|
||||||
node.generic,
|
|
||||||
schema[const.DISC_GENERIC_DEVICE_CLASS],
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if (
|
|
||||||
const.DISC_SPECIFIC_DEVICE_CLASS in schema
|
|
||||||
and node.specific not in schema[const.DISC_SPECIFIC_DEVICE_CLASS]
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"node.specific %s not in specific_device_class %s",
|
|
||||||
node.specific,
|
|
||||||
schema[const.DISC_SPECIFIC_DEVICE_CLASS],
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def check_value_schema(value, schema):
|
|
||||||
"""Check if the value matches the passed value schema."""
|
|
||||||
if (
|
|
||||||
const.DISC_COMMAND_CLASS in schema
|
|
||||||
and value.command_class not in schema[const.DISC_COMMAND_CLASS]
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"value.command_class %s not in command_class %s",
|
|
||||||
value.command_class,
|
|
||||||
schema[const.DISC_COMMAND_CLASS],
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if const.DISC_TYPE in schema and value.type not in schema[const.DISC_TYPE]:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"value.type %s not in type %s", value.type, schema[const.DISC_TYPE]
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if const.DISC_GENRE in schema and value.genre not in schema[const.DISC_GENRE]:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"value.genre %s not in genre %s", value.genre, schema[const.DISC_GENRE]
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if const.DISC_INDEX in schema and value.index not in schema[const.DISC_INDEX]:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"value.index %s not in index %s", value.index, schema[const.DISC_INDEX]
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if (
|
|
||||||
const.DISC_INSTANCE in schema
|
|
||||||
and value.instance not in schema[const.DISC_INSTANCE]
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"value.instance %s not in instance %s",
|
|
||||||
value.instance,
|
|
||||||
schema[const.DISC_INSTANCE],
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if const.DISC_SCHEMAS in schema:
|
|
||||||
found = False
|
|
||||||
for schema_item in schema[const.DISC_SCHEMAS]:
|
|
||||||
found = found or check_value_schema(value, schema_item)
|
|
||||||
if not found:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def compute_value_unique_id(node, value):
|
|
||||||
"""Compute unique_id a value would get if it were to get one."""
|
|
||||||
return f"{node.node_id}-{value.object_id}"
|
|
||||||
|
|
||||||
|
|
||||||
def node_name(node):
|
|
||||||
"""Return the name of the node."""
|
|
||||||
if is_node_parsed(node):
|
|
||||||
return node.name or f"{node.manufacturer_name} {node.product_name}"
|
|
||||||
return f"Unknown Node {node.node_id}"
|
|
||||||
|
|
||||||
|
|
||||||
def node_device_id_and_name(node, instance=1):
|
|
||||||
"""Return the name and device ID for the value with the given index."""
|
|
||||||
name = node_name(node)
|
|
||||||
if instance == 1:
|
|
||||||
return ((const.DOMAIN, node.node_id), name)
|
|
||||||
name = f"{name} ({instance})"
|
|
||||||
return ((const.DOMAIN, node.node_id, instance), name)
|
|
||||||
|
|
||||||
|
|
||||||
async def check_has_unique_id(entity, ready_callback, timeout_callback):
|
|
||||||
"""Wait for entity to have unique_id."""
|
|
||||||
start_time = dt_util.utcnow()
|
|
||||||
while True:
|
|
||||||
waited = int((dt_util.utcnow() - start_time).total_seconds())
|
|
||||||
if entity.unique_id:
|
|
||||||
ready_callback(waited)
|
|
||||||
return
|
|
||||||
if waited >= const.NODE_READY_WAIT_SECS:
|
|
||||||
# Wait up to NODE_READY_WAIT_SECS seconds for unique_id to appear.
|
|
||||||
timeout_callback(waited)
|
|
||||||
return
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
def is_node_parsed(node):
|
|
||||||
"""Check whether the node has been parsed or still waiting to be parsed."""
|
|
||||||
return bool((node.manufacturer_name and node.product_name) or node.name)
|
|
|
@ -1,90 +0,0 @@
|
||||||
"""Web socket API for Z-Wave."""
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.components import websocket_api
|
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT
|
|
||||||
from homeassistant.core import callback
|
|
||||||
|
|
||||||
from .const import (
|
|
||||||
CONF_AUTOHEAL,
|
|
||||||
CONF_DEBUG,
|
|
||||||
CONF_NETWORK_KEY,
|
|
||||||
CONF_POLLING_INTERVAL,
|
|
||||||
CONF_USB_STICK_PATH,
|
|
||||||
DATA_NETWORK,
|
|
||||||
DATA_ZWAVE_CONFIG,
|
|
||||||
)
|
|
||||||
|
|
||||||
TYPE = "type"
|
|
||||||
ID = "id"
|
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.require_admin
|
|
||||||
@websocket_api.websocket_command({vol.Required(TYPE): "zwave/network_status"})
|
|
||||||
def websocket_network_status(hass, connection, msg):
|
|
||||||
"""Get Z-Wave network status."""
|
|
||||||
network = hass.data[DATA_NETWORK]
|
|
||||||
connection.send_result(msg[ID], {"state": network.state})
|
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.require_admin
|
|
||||||
@websocket_api.websocket_command({vol.Required(TYPE): "zwave/get_config"})
|
|
||||||
def websocket_get_config(hass, connection, msg):
|
|
||||||
"""Get Z-Wave configuration."""
|
|
||||||
config = hass.data[DATA_ZWAVE_CONFIG]
|
|
||||||
connection.send_result(
|
|
||||||
msg[ID],
|
|
||||||
{
|
|
||||||
CONF_AUTOHEAL: config[CONF_AUTOHEAL],
|
|
||||||
CONF_DEBUG: config[CONF_DEBUG],
|
|
||||||
CONF_POLLING_INTERVAL: config[CONF_POLLING_INTERVAL],
|
|
||||||
CONF_USB_STICK_PATH: config[CONF_USB_STICK_PATH],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.require_admin
|
|
||||||
@websocket_api.websocket_command({vol.Required(TYPE): "zwave/get_migration_config"})
|
|
||||||
def websocket_get_migration_config(hass, connection, msg):
|
|
||||||
"""Get Z-Wave configuration for migration."""
|
|
||||||
config = hass.data[DATA_ZWAVE_CONFIG]
|
|
||||||
connection.send_result(
|
|
||||||
msg[ID],
|
|
||||||
{
|
|
||||||
CONF_USB_STICK_PATH: config[CONF_USB_STICK_PATH],
|
|
||||||
CONF_NETWORK_KEY: config[CONF_NETWORK_KEY],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.require_admin
|
|
||||||
@websocket_api.websocket_command(
|
|
||||||
{vol.Required(TYPE): "zwave/start_zwave_js_config_flow"}
|
|
||||||
)
|
|
||||||
@websocket_api.async_response
|
|
||||||
async def websocket_start_zwave_js_config_flow(hass, connection, msg):
|
|
||||||
"""Start the Z-Wave JS integration config flow (for migration wizard).
|
|
||||||
|
|
||||||
Return data with the flow id of the started Z-Wave JS config flow.
|
|
||||||
"""
|
|
||||||
config = hass.data[DATA_ZWAVE_CONFIG]
|
|
||||||
data = {
|
|
||||||
"usb_path": config[CONF_USB_STICK_PATH],
|
|
||||||
"network_key": config[CONF_NETWORK_KEY],
|
|
||||||
}
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
"zwave_js", context={"source": SOURCE_IMPORT}, data=data
|
|
||||||
)
|
|
||||||
connection.send_result(
|
|
||||||
msg[ID],
|
|
||||||
{"flow_id": result["flow_id"]},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_load_websocket_api(hass):
|
|
||||||
"""Set up the web socket API."""
|
|
||||||
websocket_api.async_register_command(hass, websocket_network_status)
|
|
||||||
websocket_api.async_register_command(hass, websocket_get_config)
|
|
||||||
websocket_api.async_register_command(hass, websocket_get_migration_config)
|
|
||||||
websocket_api.async_register_command(hass, websocket_start_zwave_js_config_flow)
|
|
|
@ -1,170 +0,0 @@
|
||||||
"""Z-Wave workarounds."""
|
|
||||||
from . import const
|
|
||||||
|
|
||||||
# Manufacturers
|
|
||||||
FIBARO = 0x010F
|
|
||||||
GE = 0x0063
|
|
||||||
PHILIO = 0x013C
|
|
||||||
SOMFY = 0x0047
|
|
||||||
WENZHOU = 0x0118
|
|
||||||
LEVITON = 0x001D
|
|
||||||
|
|
||||||
# Product IDs
|
|
||||||
GE_FAN_CONTROLLER_12730 = 0x3034
|
|
||||||
GE_FAN_CONTROLLER_14287 = 0x3131
|
|
||||||
JASCO_FAN_CONTROLLER_14314 = 0x3138
|
|
||||||
PHILIO_SLIM_SENSOR = 0x0002
|
|
||||||
PHILIO_3_IN_1_SENSOR_GEN_4 = 0x000D
|
|
||||||
PHILIO_PAN07 = 0x0005
|
|
||||||
VIZIA_FAN_CONTROLLER_VRF01 = 0x0334
|
|
||||||
LEVITON_DECORA_FAN_CONTROLLER_ZW4SF = 0x0002
|
|
||||||
|
|
||||||
# Product Types
|
|
||||||
FGFS101_FLOOD_SENSOR_TYPE = 0x0B00
|
|
||||||
FGRM222_SHUTTER2 = 0x0301
|
|
||||||
FGR222_SHUTTER2 = 0x0302
|
|
||||||
GE_DIMMER = 0x4944
|
|
||||||
PHILIO_SWITCH = 0x0001
|
|
||||||
PHILIO_SENSOR = 0x0002
|
|
||||||
SOMFY_ZRTSI = 0x5A52
|
|
||||||
VIZIA_DIMMER = 0x1001
|
|
||||||
LEVITON_DECORA_FAN_CONTROLLER = 0x0038
|
|
||||||
|
|
||||||
# Mapping devices
|
|
||||||
PHILIO_SLIM_SENSOR_MOTION_MTII = (PHILIO, PHILIO_SENSOR, PHILIO_SLIM_SENSOR, 0)
|
|
||||||
PHILIO_3_IN_1_SENSOR_GEN_4_MOTION_MTII = (
|
|
||||||
PHILIO,
|
|
||||||
PHILIO_SENSOR,
|
|
||||||
PHILIO_3_IN_1_SENSOR_GEN_4,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
PHILIO_PAN07_MTI_INSTANCE = (PHILIO, PHILIO_SWITCH, PHILIO_PAN07, 1)
|
|
||||||
WENZHOU_SLIM_SENSOR_MOTION_MTII = (WENZHOU, PHILIO_SENSOR, PHILIO_SLIM_SENSOR, 0)
|
|
||||||
|
|
||||||
# Workarounds
|
|
||||||
WORKAROUND_NO_OFF_EVENT = "trigger_no_off_event"
|
|
||||||
WORKAROUND_NO_POSITION = "workaround_no_position"
|
|
||||||
WORKAROUND_REFRESH_NODE_ON_UPDATE = "refresh_node_on_update"
|
|
||||||
WORKAROUND_IGNORE = "workaround_ignore"
|
|
||||||
|
|
||||||
# List of workarounds by (manufacturer_id, product_type, product_id, index)
|
|
||||||
DEVICE_MAPPINGS_MTII = {
|
|
||||||
PHILIO_SLIM_SENSOR_MOTION_MTII: WORKAROUND_NO_OFF_EVENT,
|
|
||||||
PHILIO_3_IN_1_SENSOR_GEN_4_MOTION_MTII: WORKAROUND_NO_OFF_EVENT,
|
|
||||||
WENZHOU_SLIM_SENSOR_MOTION_MTII: WORKAROUND_NO_OFF_EVENT,
|
|
||||||
}
|
|
||||||
|
|
||||||
# List of workarounds by (manufacturer_id, product_type, product_id, instance)
|
|
||||||
DEVICE_MAPPINGS_MTI_INSTANCE = {
|
|
||||||
PHILIO_PAN07_MTI_INSTANCE: WORKAROUND_REFRESH_NODE_ON_UPDATE
|
|
||||||
}
|
|
||||||
|
|
||||||
SOMFY_ZRTSI_CONTROLLER_MT = (SOMFY, SOMFY_ZRTSI)
|
|
||||||
|
|
||||||
# List of workarounds by (manufacturer_id, product_type)
|
|
||||||
DEVICE_MAPPINGS_MT = {SOMFY_ZRTSI_CONTROLLER_MT: WORKAROUND_NO_POSITION}
|
|
||||||
|
|
||||||
# Component mapping devices
|
|
||||||
FIBARO_FGFS101_SENSOR_ALARM = (
|
|
||||||
FIBARO,
|
|
||||||
FGFS101_FLOOD_SENSOR_TYPE,
|
|
||||||
const.COMMAND_CLASS_SENSOR_ALARM,
|
|
||||||
)
|
|
||||||
FIBARO_FGRM222_BINARY = (FIBARO, FGRM222_SHUTTER2, const.COMMAND_CLASS_SWITCH_BINARY)
|
|
||||||
FIBARO_FGR222_BINARY = (FIBARO, FGR222_SHUTTER2, const.COMMAND_CLASS_SWITCH_BINARY)
|
|
||||||
GE_FAN_CONTROLLER_12730_MULTILEVEL = (
|
|
||||||
GE,
|
|
||||||
GE_DIMMER,
|
|
||||||
GE_FAN_CONTROLLER_12730,
|
|
||||||
const.COMMAND_CLASS_SWITCH_MULTILEVEL,
|
|
||||||
)
|
|
||||||
GE_FAN_CONTROLLER_14287_MULTILEVEL = (
|
|
||||||
GE,
|
|
||||||
GE_DIMMER,
|
|
||||||
GE_FAN_CONTROLLER_14287,
|
|
||||||
const.COMMAND_CLASS_SWITCH_MULTILEVEL,
|
|
||||||
)
|
|
||||||
JASCO_FAN_CONTROLLER_14314_MULTILEVEL = (
|
|
||||||
GE,
|
|
||||||
GE_DIMMER,
|
|
||||||
JASCO_FAN_CONTROLLER_14314,
|
|
||||||
const.COMMAND_CLASS_SWITCH_MULTILEVEL,
|
|
||||||
)
|
|
||||||
VIZIA_FAN_CONTROLLER_VRF01_MULTILEVEL = (
|
|
||||||
LEVITON,
|
|
||||||
VIZIA_DIMMER,
|
|
||||||
VIZIA_FAN_CONTROLLER_VRF01,
|
|
||||||
const.COMMAND_CLASS_SWITCH_MULTILEVEL,
|
|
||||||
)
|
|
||||||
LEVITON_FAN_CONTROLLER_ZW4SF_MULTILEVEL = (
|
|
||||||
LEVITON,
|
|
||||||
LEVITON_DECORA_FAN_CONTROLLER,
|
|
||||||
LEVITON_DECORA_FAN_CONTROLLER_ZW4SF,
|
|
||||||
const.COMMAND_CLASS_SWITCH_MULTILEVEL,
|
|
||||||
)
|
|
||||||
|
|
||||||
# List of component workarounds by
|
|
||||||
# (manufacturer_id, product_type, command_class)
|
|
||||||
DEVICE_COMPONENT_MAPPING = {
|
|
||||||
FIBARO_FGFS101_SENSOR_ALARM: "binary_sensor",
|
|
||||||
FIBARO_FGRM222_BINARY: WORKAROUND_IGNORE,
|
|
||||||
FIBARO_FGR222_BINARY: WORKAROUND_IGNORE,
|
|
||||||
}
|
|
||||||
|
|
||||||
# List of component workarounds by
|
|
||||||
# (manufacturer_id, product_type, product_id, command_class)
|
|
||||||
DEVICE_COMPONENT_MAPPING_MTI = {
|
|
||||||
GE_FAN_CONTROLLER_12730_MULTILEVEL: "fan",
|
|
||||||
GE_FAN_CONTROLLER_14287_MULTILEVEL: "fan",
|
|
||||||
JASCO_FAN_CONTROLLER_14314_MULTILEVEL: "fan",
|
|
||||||
VIZIA_FAN_CONTROLLER_VRF01_MULTILEVEL: "fan",
|
|
||||||
LEVITON_FAN_CONTROLLER_ZW4SF_MULTILEVEL: "fan",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_device_component_mapping(value):
|
|
||||||
"""Get mapping of value to another component."""
|
|
||||||
if value.node.manufacturer_id.strip() and value.node.product_type.strip():
|
|
||||||
manufacturer_id = int(value.node.manufacturer_id, 16)
|
|
||||||
product_type = int(value.node.product_type, 16)
|
|
||||||
product_id = int(value.node.product_id, 16)
|
|
||||||
result = DEVICE_COMPONENT_MAPPING.get(
|
|
||||||
(manufacturer_id, product_type, value.command_class)
|
|
||||||
)
|
|
||||||
if result:
|
|
||||||
return result
|
|
||||||
|
|
||||||
result = DEVICE_COMPONENT_MAPPING_MTI.get(
|
|
||||||
(manufacturer_id, product_type, product_id, value.command_class)
|
|
||||||
)
|
|
||||||
if result:
|
|
||||||
return result
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_device_mapping(value):
|
|
||||||
"""Get mapping of value to a workaround."""
|
|
||||||
if (
|
|
||||||
value.node.manufacturer_id.strip()
|
|
||||||
and value.node.product_id.strip()
|
|
||||||
and value.node.product_type.strip()
|
|
||||||
):
|
|
||||||
manufacturer_id = int(value.node.manufacturer_id, 16)
|
|
||||||
product_type = int(value.node.product_type, 16)
|
|
||||||
product_id = int(value.node.product_id, 16)
|
|
||||||
result = DEVICE_MAPPINGS_MTII.get(
|
|
||||||
(manufacturer_id, product_type, product_id, value.index)
|
|
||||||
)
|
|
||||||
if result:
|
|
||||||
return result
|
|
||||||
|
|
||||||
result = DEVICE_MAPPINGS_MTI_INSTANCE.get(
|
|
||||||
(manufacturer_id, product_type, product_id, value.instance)
|
|
||||||
)
|
|
||||||
if result:
|
|
||||||
return result
|
|
||||||
|
|
||||||
return DEVICE_MAPPINGS_MT.get((manufacturer_id, product_type))
|
|
||||||
|
|
||||||
return None
|
|
|
@ -394,7 +394,6 @@ FLOWS = [
|
||||||
"youless",
|
"youless",
|
||||||
"zerproc",
|
"zerproc",
|
||||||
"zha",
|
"zha",
|
||||||
"zwave",
|
|
||||||
"zwave_js",
|
"zwave_js",
|
||||||
"zwave_me"
|
"zwave_me"
|
||||||
]
|
]
|
||||||
|
|
9
mypy.ini
9
mypy.ini
|
@ -2917,12 +2917,3 @@ ignore_errors = true
|
||||||
|
|
||||||
[mypy-homeassistant.components.zha.switch]
|
[mypy-homeassistant.components.zha.switch]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
[mypy-homeassistant.components.zwave]
|
|
||||||
ignore_errors = true
|
|
||||||
|
|
||||||
[mypy-homeassistant.components.zwave.migration]
|
|
||||||
ignore_errors = true
|
|
||||||
|
|
||||||
[mypy-homeassistant.components.zwave.node_entity]
|
|
||||||
ignore_errors = true
|
|
||||||
|
|
|
@ -811,9 +811,6 @@ holidays==0.13
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20220317.0
|
home-assistant-frontend==20220317.0
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
|
||||||
# homeassistant-pyozw==0.1.10
|
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.0
|
homeconnect==0.7.0
|
||||||
|
|
||||||
|
@ -1424,9 +1421,6 @@ pydelijn==1.0.0
|
||||||
# homeassistant.components.dexcom
|
# homeassistant.components.dexcom
|
||||||
pydexcom==0.2.3
|
pydexcom==0.2.3
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
|
||||||
pydispatcher==2.0.5
|
|
||||||
|
|
||||||
# homeassistant.components.doods
|
# homeassistant.components.doods
|
||||||
pydoods==1.0.2
|
pydoods==1.0.2
|
||||||
|
|
||||||
|
|
|
@ -561,9 +561,6 @@ holidays==0.13
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20220317.0
|
home-assistant-frontend==20220317.0
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
|
||||||
# homeassistant-pyozw==0.1.10
|
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.0
|
homeconnect==0.7.0
|
||||||
|
|
||||||
|
@ -931,9 +928,6 @@ pydeconz==87
|
||||||
# homeassistant.components.dexcom
|
# homeassistant.components.dexcom
|
||||||
pydexcom==0.2.3
|
pydexcom==0.2.3
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
|
||||||
pydispatcher==2.0.5
|
|
||||||
|
|
||||||
# homeassistant.components.econet
|
# homeassistant.components.econet
|
||||||
pyeconet==0.1.15
|
pyeconet==0.1.15
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ COMMENT_REQUIREMENTS = (
|
||||||
"decora_wifi",
|
"decora_wifi",
|
||||||
"evdev",
|
"evdev",
|
||||||
"face_recognition",
|
"face_recognition",
|
||||||
"homeassistant-pyozw",
|
|
||||||
"opencv-python-headless",
|
"opencv-python-headless",
|
||||||
"pybluez",
|
"pybluez",
|
||||||
"pycups",
|
"pycups",
|
||||||
|
|
|
@ -214,9 +214,6 @@ IGNORED_MODULES: Final[list[str]] = [
|
||||||
"homeassistant.components.zha.sensor",
|
"homeassistant.components.zha.sensor",
|
||||||
"homeassistant.components.zha.siren",
|
"homeassistant.components.zha.siren",
|
||||||
"homeassistant.components.zha.switch",
|
"homeassistant.components.zha.switch",
|
||||||
"homeassistant.components.zwave",
|
|
||||||
"homeassistant.components.zwave.migration",
|
|
||||||
"homeassistant.components.zwave.node_entity",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Component modules which should set no_implicit_reexport = true.
|
# Component modules which should set no_implicit_reexport = true.
|
||||||
|
|
|
@ -237,7 +237,7 @@ STATE_REWRITE = {
|
||||||
"[%key:state::lock::unlocked%]": "[%key:common::state::unlocked%]",
|
"[%key:state::lock::unlocked%]": "[%key:common::state::unlocked%]",
|
||||||
}
|
}
|
||||||
SKIP_DOMAIN = {"default", "scene"}
|
SKIP_DOMAIN = {"default", "scene"}
|
||||||
STATES_WITH_DEV_CLASS = {"binary_sensor", "zwave"}
|
STATES_WITH_DEV_CLASS = {"binary_sensor"}
|
||||||
GROUP_DELETE = {"opening", "closing", "stopped"} # They don't exist
|
GROUP_DELETE = {"opening", "closing", "stopped"} # They don't exist
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,8 @@
|
||||||
"""Test config init."""
|
"""Test config init."""
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components import config
|
|
||||||
from homeassistant.const import EVENT_COMPONENT_LOADED
|
|
||||||
from homeassistant.setup import ATTR_COMPONENT, async_setup_component
|
|
||||||
|
|
||||||
from tests.common import mock_component
|
|
||||||
|
|
||||||
|
|
||||||
async def test_config_setup(hass, loop):
|
async def test_config_setup(hass, loop):
|
||||||
"""Test it sets up hassbian."""
|
"""Test it sets up hassbian."""
|
||||||
await async_setup_component(hass, "config", {})
|
await async_setup_component(hass, "config", {})
|
||||||
assert "config" in hass.config.components
|
assert "config" in hass.config.components
|
||||||
|
|
||||||
|
|
||||||
async def test_load_on_demand_already_loaded(hass, aiohttp_client):
|
|
||||||
"""Test getting suites."""
|
|
||||||
mock_component(hass, "zwave")
|
|
||||||
|
|
||||||
with patch.object(config, "SECTIONS", []), patch.object(
|
|
||||||
config, "ON_DEMAND", ["zwave"]
|
|
||||||
), patch(
|
|
||||||
"homeassistant.components.config.zwave.async_setup", return_value=True
|
|
||||||
) as stp:
|
|
||||||
|
|
||||||
await async_setup_component(hass, "config", {})
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert stp.called
|
|
||||||
|
|
||||||
|
|
||||||
async def test_load_on_demand_on_load(hass, aiohttp_client):
|
|
||||||
"""Test getting suites."""
|
|
||||||
with patch.object(config, "SECTIONS", []), patch.object(
|
|
||||||
config, "ON_DEMAND", ["zwave"]
|
|
||||||
):
|
|
||||||
await async_setup_component(hass, "config", {})
|
|
||||||
|
|
||||||
assert "config.zwave" not in hass.config.components
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.config.zwave.async_setup", return_value=True
|
|
||||||
) as stp:
|
|
||||||
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: "zwave"})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert stp.called
|
|
||||||
|
|
|
@ -1,542 +0,0 @@
|
||||||
"""Test Z-Wave config panel."""
|
|
||||||
from http import HTTPStatus
|
|
||||||
import json
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.bootstrap import async_setup_component
|
|
||||||
from homeassistant.components import config
|
|
||||||
from homeassistant.components.zwave import DATA_NETWORK, const
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue
|
|
||||||
|
|
||||||
VIEW_NAME = "api:config:zwave:device_config"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def client(loop, hass, hass_client):
|
|
||||||
"""Client to communicate with Z-Wave config views."""
|
|
||||||
with patch.object(config, "SECTIONS", ["zwave"]):
|
|
||||||
loop.run_until_complete(async_setup_component(hass, "config", {}))
|
|
||||||
|
|
||||||
return loop.run_until_complete(hass_client())
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_device_config(client):
|
|
||||||
"""Test getting device config."""
|
|
||||||
|
|
||||||
def mock_read(path):
|
|
||||||
"""Mock reading data."""
|
|
||||||
return {"hello.beer": {"free": "beer"}, "other.entity": {"do": "something"}}
|
|
||||||
|
|
||||||
with patch("homeassistant.components.config._read", mock_read):
|
|
||||||
resp = await client.get("/api/config/zwave/device_config/hello.beer")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {"free": "beer"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_device_config(client):
|
|
||||||
"""Test updating device config."""
|
|
||||||
orig_data = {
|
|
||||||
"hello.beer": {"ignored": True},
|
|
||||||
"other.entity": {"polling_intensity": 2},
|
|
||||||
}
|
|
||||||
|
|
||||||
def mock_read(path):
|
|
||||||
"""Mock reading data."""
|
|
||||||
return orig_data
|
|
||||||
|
|
||||||
written = []
|
|
||||||
|
|
||||||
def mock_write(path, data):
|
|
||||||
"""Mock writing data."""
|
|
||||||
written.append(data)
|
|
||||||
|
|
||||||
with patch("homeassistant.components.config._read", mock_read), patch(
|
|
||||||
"homeassistant.components.config._write", mock_write
|
|
||||||
):
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/config/zwave/device_config/hello.beer",
|
|
||||||
data=json.dumps({"polling_intensity": 2}),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
assert result == {"result": "ok"}
|
|
||||||
|
|
||||||
orig_data["hello.beer"]["polling_intensity"] = 2
|
|
||||||
|
|
||||||
assert written[0] == orig_data
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_device_config_invalid_key(client):
|
|
||||||
"""Test updating device config."""
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/config/zwave/device_config/invalid_entity",
|
|
||||||
data=json.dumps({"polling_intensity": 2}),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.BAD_REQUEST
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_device_config_invalid_data(client):
|
|
||||||
"""Test updating device config."""
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/config/zwave/device_config/hello.beer",
|
|
||||||
data=json.dumps({"invalid_option": 2}),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.BAD_REQUEST
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_device_config_invalid_json(client):
|
|
||||||
"""Test updating device config."""
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/config/zwave/device_config/hello.beer", data="not json"
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.BAD_REQUEST
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_values(hass, client):
|
|
||||||
"""Test getting values on node."""
|
|
||||||
node = MockNode(node_id=1)
|
|
||||||
value = MockValue(
|
|
||||||
value_id=123456,
|
|
||||||
node=node,
|
|
||||||
label="Test Label",
|
|
||||||
instance=1,
|
|
||||||
index=2,
|
|
||||||
poll_intensity=4,
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
node2 = MockNode(node_id=2)
|
|
||||||
value2 = MockValue(value_id=234567, node=node2, label="Test Label 2")
|
|
||||||
values2 = MockEntityValues(primary=value2)
|
|
||||||
hass.data[const.DATA_ENTITY_VALUES] = [values, values2]
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/values/1")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {
|
|
||||||
"123456": {
|
|
||||||
"label": "Test Label",
|
|
||||||
"instance": 1,
|
|
||||||
"index": 2,
|
|
||||||
"poll_intensity": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_groups(hass, client):
|
|
||||||
"""Test getting groupdata on node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=2)
|
|
||||||
node.groups.associations = "assoc"
|
|
||||||
node.groups.associations_instances = "inst"
|
|
||||||
node.groups.label = "the label"
|
|
||||||
node.groups.max_associations = "max"
|
|
||||||
node.groups = {1: node.groups}
|
|
||||||
network.nodes = {2: node}
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/groups/2")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {
|
|
||||||
"1": {
|
|
||||||
"association_instances": "inst",
|
|
||||||
"associations": "assoc",
|
|
||||||
"label": "the label",
|
|
||||||
"max_associations": "max",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_groups_nogroups(hass, client):
|
|
||||||
"""Test getting groupdata on node with no groups."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=2)
|
|
||||||
|
|
||||||
network.nodes = {2: node}
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/groups/2")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_groups_nonode(hass, client):
|
|
||||||
"""Test getting groupdata on nonexisting node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
network.nodes = {1: 1, 5: 5}
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/groups/2")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.NOT_FOUND
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {"message": "Node not found"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_config(hass, client):
|
|
||||||
"""Test getting config on node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=2)
|
|
||||||
value = MockValue(index=12, command_class=const.COMMAND_CLASS_CONFIGURATION)
|
|
||||||
value.label = "label"
|
|
||||||
value.help = "help"
|
|
||||||
value.type = "type"
|
|
||||||
value.data = "data"
|
|
||||||
value.data_items = ["item1", "item2"]
|
|
||||||
value.max = "max"
|
|
||||||
value.min = "min"
|
|
||||||
node.values = {12: value}
|
|
||||||
network.nodes = {2: node}
|
|
||||||
node.get_values.return_value = node.values
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/config/2")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {
|
|
||||||
"12": {
|
|
||||||
"data": "data",
|
|
||||||
"data_items": ["item1", "item2"],
|
|
||||||
"help": "help",
|
|
||||||
"label": "label",
|
|
||||||
"max": "max",
|
|
||||||
"min": "min",
|
|
||||||
"type": "type",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_config_noconfig_node(hass, client):
|
|
||||||
"""Test getting config on node without config."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=2)
|
|
||||||
|
|
||||||
network.nodes = {2: node}
|
|
||||||
node.get_values.return_value = node.values
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/config/2")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_config_nonode(hass, client):
|
|
||||||
"""Test getting config on nonexisting node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
network.nodes = {1: 1, 5: 5}
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/config/2")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.NOT_FOUND
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {"message": "Node not found"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_usercodes_nonode(hass, client):
|
|
||||||
"""Test getting usercodes on nonexisting node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
network.nodes = {1: 1, 5: 5}
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/usercodes/2")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.NOT_FOUND
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {"message": "Node not found"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_usercodes(hass, client):
|
|
||||||
"""Test getting usercodes on node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18, command_classes=[const.COMMAND_CLASS_USER_CODE])
|
|
||||||
value = MockValue(index=0, command_class=const.COMMAND_CLASS_USER_CODE)
|
|
||||||
value.genre = const.GENRE_USER
|
|
||||||
value.label = "label"
|
|
||||||
value.data = "1234"
|
|
||||||
node.values = {0: value}
|
|
||||||
network.nodes = {18: node}
|
|
||||||
node.get_values.return_value = node.values
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/usercodes/18")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {"0": {"code": "1234", "label": "label", "length": 4}}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_usercode_nousercode_node(hass, client):
|
|
||||||
"""Test getting usercodes on node without usercodes."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18)
|
|
||||||
|
|
||||||
network.nodes = {18: node}
|
|
||||||
node.get_values.return_value = node.values
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/usercodes/18")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_usercodes_no_genreuser(hass, client):
|
|
||||||
"""Test getting usercodes on node missing genre user."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18, command_classes=[const.COMMAND_CLASS_USER_CODE])
|
|
||||||
value = MockValue(index=0, command_class=const.COMMAND_CLASS_USER_CODE)
|
|
||||||
value.genre = const.GENRE_SYSTEM
|
|
||||||
value.label = "label"
|
|
||||||
value.data = "1234"
|
|
||||||
node.values = {0: value}
|
|
||||||
network.nodes = {18: node}
|
|
||||||
node.get_values.return_value = node.values
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/usercodes/18")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
|
|
||||||
assert result == {}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_save_config_no_network(hass, client):
|
|
||||||
"""Test saving configuration without network data."""
|
|
||||||
resp = await client.post("/api/zwave/saveconfig")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.NOT_FOUND
|
|
||||||
result = await resp.json()
|
|
||||||
assert result == {"message": "No Z-Wave network data found"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_save_config(hass, client):
|
|
||||||
"""Test saving configuration."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
|
|
||||||
resp = await client.post("/api/zwave/saveconfig")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
assert network.write_config.called
|
|
||||||
assert result == {"message": "Z-Wave configuration saved to file"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_protection_values(hass, client):
|
|
||||||
"""Test getting protection values on node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18, command_classes=[const.COMMAND_CLASS_PROTECTION])
|
|
||||||
value = MockValue(
|
|
||||||
value_id=123456,
|
|
||||||
index=0,
|
|
||||||
instance=1,
|
|
||||||
command_class=const.COMMAND_CLASS_PROTECTION,
|
|
||||||
)
|
|
||||||
value.label = "Protection Test"
|
|
||||||
value.data_items = [
|
|
||||||
"Unprotected",
|
|
||||||
"Protection by Sequence",
|
|
||||||
"No Operation Possible",
|
|
||||||
]
|
|
||||||
value.data = "Unprotected"
|
|
||||||
network.nodes = {18: node}
|
|
||||||
node.value = value
|
|
||||||
|
|
||||||
node.get_protection_item.return_value = "Unprotected"
|
|
||||||
node.get_protection_items.return_value = value.data_items
|
|
||||||
node.get_protections.return_value = {value.value_id: "Object"}
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/protection/18")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
assert node.get_protections.called
|
|
||||||
assert node.get_protection_item.called
|
|
||||||
assert node.get_protection_items.called
|
|
||||||
assert result == {
|
|
||||||
"value_id": "123456",
|
|
||||||
"selected": "Unprotected",
|
|
||||||
"options": ["Unprotected", "Protection by Sequence", "No Operation Possible"],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_protection_values_nonexisting_node(hass, client):
|
|
||||||
"""Test getting protection values on node with wrong nodeid."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18, command_classes=[const.COMMAND_CLASS_PROTECTION])
|
|
||||||
value = MockValue(
|
|
||||||
value_id=123456,
|
|
||||||
index=0,
|
|
||||||
instance=1,
|
|
||||||
command_class=const.COMMAND_CLASS_PROTECTION,
|
|
||||||
)
|
|
||||||
value.label = "Protection Test"
|
|
||||||
value.data_items = [
|
|
||||||
"Unprotected",
|
|
||||||
"Protection by Sequence",
|
|
||||||
"No Operation Possible",
|
|
||||||
]
|
|
||||||
value.data = "Unprotected"
|
|
||||||
network.nodes = {17: node}
|
|
||||||
node.value = value
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/protection/18")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.NOT_FOUND
|
|
||||||
result = await resp.json()
|
|
||||||
assert not node.get_protections.called
|
|
||||||
assert not node.get_protection_item.called
|
|
||||||
assert not node.get_protection_items.called
|
|
||||||
assert result == {"message": "Node not found"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_protection_values_without_protectionclass(hass, client):
|
|
||||||
"""Test getting protection values on node without protectionclass."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18)
|
|
||||||
value = MockValue(value_id=123456, index=0, instance=1)
|
|
||||||
network.nodes = {18: node}
|
|
||||||
node.value = value
|
|
||||||
|
|
||||||
resp = await client.get("/api/zwave/protection/18")
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
assert not node.get_protections.called
|
|
||||||
assert not node.get_protection_item.called
|
|
||||||
assert not node.get_protection_items.called
|
|
||||||
assert result == {}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_protection_value(hass, client):
|
|
||||||
"""Test setting protection value on node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18, command_classes=[const.COMMAND_CLASS_PROTECTION])
|
|
||||||
value = MockValue(
|
|
||||||
value_id=123456,
|
|
||||||
index=0,
|
|
||||||
instance=1,
|
|
||||||
command_class=const.COMMAND_CLASS_PROTECTION,
|
|
||||||
)
|
|
||||||
value.label = "Protection Test"
|
|
||||||
value.data_items = [
|
|
||||||
"Unprotected",
|
|
||||||
"Protection by Sequence",
|
|
||||||
"No Operation Possible",
|
|
||||||
]
|
|
||||||
value.data = "Unprotected"
|
|
||||||
network.nodes = {18: node}
|
|
||||||
node.value = value
|
|
||||||
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/zwave/protection/18",
|
|
||||||
data=json.dumps({"value_id": "123456", "selection": "Protection by Sequence"}),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.OK
|
|
||||||
result = await resp.json()
|
|
||||||
assert node.set_protection.called
|
|
||||||
assert result == {"message": "Protection setting successfully set"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_protection_value_failed(hass, client):
|
|
||||||
"""Test setting protection value failed on node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=18, command_classes=[const.COMMAND_CLASS_PROTECTION])
|
|
||||||
value = MockValue(
|
|
||||||
value_id=123456,
|
|
||||||
index=0,
|
|
||||||
instance=1,
|
|
||||||
command_class=const.COMMAND_CLASS_PROTECTION,
|
|
||||||
)
|
|
||||||
value.label = "Protection Test"
|
|
||||||
value.data_items = [
|
|
||||||
"Unprotected",
|
|
||||||
"Protection by Sequence",
|
|
||||||
"No Operation Possible",
|
|
||||||
]
|
|
||||||
value.data = "Unprotected"
|
|
||||||
network.nodes = {18: node}
|
|
||||||
node.value = value
|
|
||||||
node.set_protection.return_value = False
|
|
||||||
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/zwave/protection/18",
|
|
||||||
data=json.dumps({"value_id": "123456", "selection": "Protecton by Sequence"}),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.ACCEPTED
|
|
||||||
result = await resp.json()
|
|
||||||
assert node.set_protection.called
|
|
||||||
assert result == {"message": "Protection setting did not complete"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_protection_value_nonexisting_node(hass, client):
|
|
||||||
"""Test setting protection value on nonexisting node."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=17, command_classes=[const.COMMAND_CLASS_PROTECTION])
|
|
||||||
value = MockValue(
|
|
||||||
value_id=123456,
|
|
||||||
index=0,
|
|
||||||
instance=1,
|
|
||||||
command_class=const.COMMAND_CLASS_PROTECTION,
|
|
||||||
)
|
|
||||||
value.label = "Protection Test"
|
|
||||||
value.data_items = [
|
|
||||||
"Unprotected",
|
|
||||||
"Protection by Sequence",
|
|
||||||
"No Operation Possible",
|
|
||||||
]
|
|
||||||
value.data = "Unprotected"
|
|
||||||
network.nodes = {17: node}
|
|
||||||
node.value = value
|
|
||||||
node.set_protection.return_value = False
|
|
||||||
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/zwave/protection/18",
|
|
||||||
data=json.dumps({"value_id": "123456", "selection": "Protecton by Sequence"}),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.NOT_FOUND
|
|
||||||
result = await resp.json()
|
|
||||||
assert not node.set_protection.called
|
|
||||||
assert result == {"message": "Node not found"}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_protection_value_missing_class(hass, client):
|
|
||||||
"""Test setting protection value on node without protectionclass."""
|
|
||||||
network = hass.data[DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=17)
|
|
||||||
value = MockValue(value_id=123456, index=0, instance=1)
|
|
||||||
network.nodes = {17: node}
|
|
||||||
node.value = value
|
|
||||||
node.set_protection.return_value = False
|
|
||||||
|
|
||||||
resp = await client.post(
|
|
||||||
"/api/zwave/protection/17",
|
|
||||||
data=json.dumps({"value_id": "123456", "selection": "Protecton by Sequence"}),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status == HTTPStatus.NOT_FOUND
|
|
||||||
result = await resp.json()
|
|
||||||
assert not node.set_protection.called
|
|
||||||
assert result == {"message": "No protection commandclass on this node"}
|
|
|
@ -1 +0,0 @@
|
||||||
"""Tests for the Z-Wave component."""
|
|
|
@ -1,80 +0,0 @@
|
||||||
"""Fixtures for Z-Wave tests."""
|
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.zwave import const
|
|
||||||
|
|
||||||
from tests.components.light.conftest import mock_light_profiles # noqa: F401
|
|
||||||
from tests.mock.zwave import MockNetwork, MockNode, MockOption, MockValue
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_openzwave():
|
|
||||||
"""Mock out Open Z-Wave."""
|
|
||||||
base_mock = MagicMock()
|
|
||||||
libopenzwave = base_mock.libopenzwave
|
|
||||||
libopenzwave.__file__ = "test"
|
|
||||||
base_mock.network.ZWaveNetwork = MockNetwork
|
|
||||||
base_mock.option.ZWaveOption = MockOption
|
|
||||||
|
|
||||||
with patch.dict(
|
|
||||||
"sys.modules",
|
|
||||||
{
|
|
||||||
"libopenzwave": libopenzwave,
|
|
||||||
"openzwave.option": base_mock.option,
|
|
||||||
"openzwave.network": base_mock.network,
|
|
||||||
"openzwave.group": base_mock.group,
|
|
||||||
},
|
|
||||||
):
|
|
||||||
yield base_mock
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_discovery():
|
|
||||||
"""Mock discovery."""
|
|
||||||
discovery = MagicMock()
|
|
||||||
discovery.async_load_platform = AsyncMock(return_value=None)
|
|
||||||
yield discovery
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_import_module():
|
|
||||||
"""Mock import module."""
|
|
||||||
platform = MagicMock()
|
|
||||||
mock_device = MagicMock()
|
|
||||||
mock_device.name = "test_device"
|
|
||||||
platform.get_device.return_value = mock_device
|
|
||||||
|
|
||||||
import_module = MagicMock()
|
|
||||||
import_module.return_value = platform
|
|
||||||
yield import_module
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_values():
|
|
||||||
"""Mock values."""
|
|
||||||
node = MockNode()
|
|
||||||
mock_schema = {
|
|
||||||
const.DISC_COMPONENT: "mock_component",
|
|
||||||
const.DISC_VALUES: {
|
|
||||||
const.DISC_PRIMARY: {const.DISC_COMMAND_CLASS: ["mock_primary_class"]},
|
|
||||||
"secondary": {const.DISC_COMMAND_CLASS: ["mock_secondary_class"]},
|
|
||||||
"optional": {
|
|
||||||
const.DISC_COMMAND_CLASS: ["mock_optional_class"],
|
|
||||||
const.DISC_OPTIONAL: True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
value_class = MagicMock()
|
|
||||||
value_class.primary = MockValue(
|
|
||||||
command_class="mock_primary_class", node=node, value_id=1000
|
|
||||||
)
|
|
||||||
value_class.secondary = MockValue(command_class="mock_secondary_class", node=node)
|
|
||||||
value_class.duplicate_secondary = MockValue(
|
|
||||||
command_class="mock_secondary_class", node=node
|
|
||||||
)
|
|
||||||
value_class.optional = MockValue(command_class="mock_optional_class", node=node)
|
|
||||||
value_class.no_match_value = MockValue(command_class="mock_bad_class", node=node)
|
|
||||||
|
|
||||||
yield (node, value_class, mock_schema)
|
|
|
@ -1,103 +0,0 @@
|
||||||
"""Test Z-Wave binary sensors."""
|
|
||||||
import datetime
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.zwave import binary_sensor, const
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_none(mock_openzwave):
|
|
||||||
"""Test device is not returned."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=False, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = binary_sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert device is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_trigger_sensor(mock_openzwave):
|
|
||||||
"""Test device is a trigger sensor."""
|
|
||||||
node = MockNode(manufacturer_id="013c", product_type="0002", product_id="0002")
|
|
||||||
value = MockValue(data=False, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = binary_sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, binary_sensor.ZWaveTriggerSensor)
|
|
||||||
assert device.device_class == "motion"
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_workaround_sensor(mock_openzwave):
|
|
||||||
"""Test that workaround returns a binary sensor."""
|
|
||||||
node = MockNode(manufacturer_id="010f", product_type="0b00")
|
|
||||||
value = MockValue(
|
|
||||||
data=False, node=node, command_class=const.COMMAND_CLASS_SENSOR_ALARM
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = binary_sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, binary_sensor.ZWaveBinarySensor)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_sensor(mock_openzwave):
|
|
||||||
"""Test that device returns a binary sensor."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=False, node=node, command_class=const.COMMAND_CLASS_SENSOR_BINARY
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = binary_sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, binary_sensor.ZWaveBinarySensor)
|
|
||||||
|
|
||||||
|
|
||||||
def test_binary_sensor_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for binary sensor."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=False, node=node, command_class=const.COMMAND_CLASS_SENSOR_BINARY
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
device = binary_sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert not device.is_on
|
|
||||||
|
|
||||||
value.data = True
|
|
||||||
value_changed(value)
|
|
||||||
|
|
||||||
assert device.is_on
|
|
||||||
|
|
||||||
|
|
||||||
async def test_trigger_sensor_value_changed(hass, mock_openzwave):
|
|
||||||
"""Test value changed for trigger sensor."""
|
|
||||||
node = MockNode(manufacturer_id="013c", product_type="0002", product_id="0002")
|
|
||||||
value = MockValue(data=False, node=node)
|
|
||||||
value_off_delay = MockValue(data=15, node=node)
|
|
||||||
values = MockEntityValues(primary=value, off_delay=value_off_delay)
|
|
||||||
device = binary_sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert not device.is_on
|
|
||||||
|
|
||||||
value.data = True
|
|
||||||
await hass.async_add_executor_job(value_changed, value)
|
|
||||||
assert device.invalidate_after is None
|
|
||||||
|
|
||||||
device.hass = hass
|
|
||||||
|
|
||||||
value.data = True
|
|
||||||
await hass.async_add_executor_job(value_changed, value)
|
|
||||||
assert device.is_on
|
|
||||||
|
|
||||||
test_time = device.invalidate_after - datetime.timedelta(seconds=1)
|
|
||||||
with patch("homeassistant.util.dt.utcnow", return_value=test_time):
|
|
||||||
assert device.is_on
|
|
||||||
|
|
||||||
test_time = device.invalidate_after
|
|
||||||
with patch("homeassistant.util.dt.utcnow", return_value=test_time):
|
|
||||||
assert not device.is_on
|
|
|
@ -1,893 +0,0 @@
|
||||||
"""Test Z-Wave climate devices."""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.climate.const import (
|
|
||||||
ATTR_TARGET_TEMP_HIGH,
|
|
||||||
ATTR_TARGET_TEMP_LOW,
|
|
||||||
CURRENT_HVAC_COOL,
|
|
||||||
CURRENT_HVAC_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODES,
|
|
||||||
PRESET_AWAY,
|
|
||||||
PRESET_BOOST,
|
|
||||||
PRESET_ECO,
|
|
||||||
PRESET_NONE,
|
|
||||||
SUPPORT_AUX_HEAT,
|
|
||||||
SUPPORT_FAN_MODE,
|
|
||||||
SUPPORT_PRESET_MODE,
|
|
||||||
SUPPORT_SWING_MODE,
|
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
|
||||||
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
|
||||||
)
|
|
||||||
from homeassistant.components.zwave import climate, const
|
|
||||||
from homeassistant.components.zwave.climate import (
|
|
||||||
AUX_HEAT_ZWAVE_MODE,
|
|
||||||
DEFAULT_HVAC_MODES,
|
|
||||||
)
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT,
|
|
||||||
data_items=[
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=1, node=node),
|
|
||||||
setpoint_cooling=MockValue(data=10, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data=CURRENT_HVAC_HEAT, node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_zxt_120(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device."""
|
|
||||||
node = MockNode(manufacturer_id="5254", product_id="8377")
|
|
||||||
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT,
|
|
||||||
data_items=[
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=1, node=node),
|
|
||||||
setpoint_cooling=MockValue(data=10, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data=CURRENT_HVAC_HEAT, node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
zxt_120_swing_mode=MockValue(data="test3", data_items=[6, 7, 8], node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_mapping(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device. Test state mapping."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data="Heat",
|
|
||||||
data_items=["Off", "Cool", "Heat", "Full Power", "Auto"],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=1, node=node),
|
|
||||||
setpoint_cooling=MockValue(data=10, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data="heating", node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_unknown(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device. Test state unknown."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data="Heat",
|
|
||||||
data_items=["Off", "Cool", "Heat", "heat_cool", "Abcdefg"],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=1, node=node),
|
|
||||||
setpoint_cooling=MockValue(data=10, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data="test4", node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_heat_cool(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device. Test state heat only."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT,
|
|
||||||
data_items=[
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
"Heat Eco",
|
|
||||||
"Cool Eco",
|
|
||||||
],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=1, node=node),
|
|
||||||
setpoint_cooling=MockValue(data=10, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data="test4", node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_heat_cool_range(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device. Target range mode."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT_COOL,
|
|
||||||
data_items=[
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=1, node=node),
|
|
||||||
setpoint_cooling=MockValue(data=10, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data="test4", node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_heat_cool_away(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device. Target range mode."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT_COOL,
|
|
||||||
data_items=[
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
PRESET_AWAY,
|
|
||||||
],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=2, node=node),
|
|
||||||
setpoint_cooling=MockValue(data=9, node=node),
|
|
||||||
setpoint_away_heating=MockValue(data=1, node=node),
|
|
||||||
setpoint_away_cooling=MockValue(data=10, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data="test4", node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_heat_eco(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device. heat/heat eco."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT,
|
|
||||||
data_items=[HVAC_MODE_OFF, HVAC_MODE_HEAT, "heat econ"],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=2, node=node),
|
|
||||||
setpoint_eco_heating=MockValue(data=1, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data="test4", node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_aux_heat(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device. aux heat."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT,
|
|
||||||
data_items=[HVAC_MODE_OFF, HVAC_MODE_HEAT, "Aux Heat"],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
setpoint_heating=MockValue(data=2, node=node),
|
|
||||||
setpoint_eco_heating=MockValue(data=1, node=node),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data="test4", node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_single_setpoint(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device.
|
|
||||||
|
|
||||||
SETPOINT_THERMOSTAT device class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_SETPOINT, data=1, node=node
|
|
||||||
),
|
|
||||||
mode=None,
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data=CURRENT_HVAC_HEAT, node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def device_single_setpoint_with_mode(hass, mock_openzwave):
|
|
||||||
"""Fixture to provide a precreated climate device.
|
|
||||||
|
|
||||||
SETPOINT_THERMOSTAT device class with COMMAND_CLASS_THERMOSTAT_MODE command class
|
|
||||||
"""
|
|
||||||
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_SETPOINT, data=1, node=node
|
|
||||||
),
|
|
||||||
mode=MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_THERMOSTAT_MODE,
|
|
||||||
data=HVAC_MODE_HEAT,
|
|
||||||
data_items=[HVAC_MODE_OFF, HVAC_MODE_HEAT],
|
|
||||||
node=node,
|
|
||||||
),
|
|
||||||
temperature=MockValue(data=5, node=node, units=None),
|
|
||||||
fan_mode=MockValue(data="test2", data_items=[3, 4, 5], node=node),
|
|
||||||
operating_state=MockValue(data=CURRENT_HVAC_HEAT, node=node),
|
|
||||||
fan_action=MockValue(data=7, node=node),
|
|
||||||
)
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
yield device
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_none(hass, mock_openzwave):
|
|
||||||
"""Test get_device returns None."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = climate.get_device(hass, node=node, values=values, node_config={})
|
|
||||||
assert device is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_multiple_setpoint_device(device):
|
|
||||||
"""Test get_device returns a Z-Wave multiple setpoint device."""
|
|
||||||
assert isinstance(device, climate.ZWaveClimateMultipleSetpoint)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_single_setpoint_device(device_single_setpoint):
|
|
||||||
"""Test get_device returns a Z-Wave single setpoint device."""
|
|
||||||
assert isinstance(device_single_setpoint, climate.ZWaveClimateSingleSetpoint)
|
|
||||||
|
|
||||||
|
|
||||||
def test_default_hvac_modes():
|
|
||||||
"""Test whether all hvac modes are included in default_hvac_modes."""
|
|
||||||
for hvac_mode in HVAC_MODES:
|
|
||||||
assert hvac_mode in DEFAULT_HVAC_MODES
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features(device):
|
|
||||||
"""Test supported features flags."""
|
|
||||||
assert (
|
|
||||||
device.supported_features
|
|
||||||
== SUPPORT_FAN_MODE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features_temp_range(device_heat_cool_range):
|
|
||||||
"""Test supported features flags with target temp range."""
|
|
||||||
device = device_heat_cool_range
|
|
||||||
assert (
|
|
||||||
device.supported_features
|
|
||||||
== SUPPORT_FAN_MODE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features_preset_mode(device_mapping):
|
|
||||||
"""Test supported features flags with swing mode."""
|
|
||||||
device = device_mapping
|
|
||||||
assert (
|
|
||||||
device.supported_features
|
|
||||||
== SUPPORT_FAN_MODE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
+ SUPPORT_PRESET_MODE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features_preset_mode_away(device_heat_cool_away):
|
|
||||||
"""Test supported features flags with swing mode."""
|
|
||||||
device = device_heat_cool_away
|
|
||||||
assert (
|
|
||||||
device.supported_features
|
|
||||||
== SUPPORT_FAN_MODE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
+ SUPPORT_PRESET_MODE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features_swing_mode(device_zxt_120):
|
|
||||||
"""Test supported features flags with swing mode."""
|
|
||||||
device = device_zxt_120
|
|
||||||
assert (
|
|
||||||
device.supported_features
|
|
||||||
== SUPPORT_FAN_MODE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE
|
|
||||||
+ SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
+ SUPPORT_SWING_MODE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features_aux_heat(device_aux_heat):
|
|
||||||
"""Test supported features flags with aux heat."""
|
|
||||||
device = device_aux_heat
|
|
||||||
assert (
|
|
||||||
device.supported_features
|
|
||||||
== SUPPORT_FAN_MODE + SUPPORT_TARGET_TEMPERATURE + SUPPORT_AUX_HEAT
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features_single_setpoint(device_single_setpoint):
|
|
||||||
"""Test supported features flags for SETPOINT_THERMOSTAT."""
|
|
||||||
device = device_single_setpoint
|
|
||||||
assert device.supported_features == SUPPORT_FAN_MODE + SUPPORT_TARGET_TEMPERATURE
|
|
||||||
|
|
||||||
|
|
||||||
def test_supported_features_single_setpoint_with_mode(device_single_setpoint_with_mode):
|
|
||||||
"""Test supported features flags for SETPOINT_THERMOSTAT."""
|
|
||||||
device = device_single_setpoint_with_mode
|
|
||||||
assert device.supported_features == SUPPORT_FAN_MODE + SUPPORT_TARGET_TEMPERATURE
|
|
||||||
|
|
||||||
|
|
||||||
def test_zxt_120_swing_mode(device_zxt_120):
|
|
||||||
"""Test operation of the zxt 120 swing mode."""
|
|
||||||
device = device_zxt_120
|
|
||||||
|
|
||||||
assert device.swing_modes == [6, 7, 8]
|
|
||||||
assert device._zxt_120 == 1
|
|
||||||
|
|
||||||
# Test set mode
|
|
||||||
assert device.values.zxt_120_swing_mode.data == "test3"
|
|
||||||
device.set_swing_mode("test_swing_set")
|
|
||||||
assert device.values.zxt_120_swing_mode.data == "test_swing_set"
|
|
||||||
|
|
||||||
# Test mode changed
|
|
||||||
value_changed(device.values.zxt_120_swing_mode)
|
|
||||||
assert device.swing_mode == "test_swing_set"
|
|
||||||
device.values.zxt_120_swing_mode.data = "test_swing_updated"
|
|
||||||
value_changed(device.values.zxt_120_swing_mode)
|
|
||||||
assert device.swing_mode == "test_swing_updated"
|
|
||||||
|
|
||||||
|
|
||||||
def test_temperature_unit(device):
|
|
||||||
"""Test temperature unit."""
|
|
||||||
assert device.temperature_unit == TEMP_CELSIUS
|
|
||||||
device.values.temperature.units = "F"
|
|
||||||
value_changed(device.values.temperature)
|
|
||||||
assert device.temperature_unit == TEMP_FAHRENHEIT
|
|
||||||
device.values.temperature.units = "C"
|
|
||||||
value_changed(device.values.temperature)
|
|
||||||
assert device.temperature_unit == TEMP_CELSIUS
|
|
||||||
|
|
||||||
|
|
||||||
def test_data_lists(device):
|
|
||||||
"""Test data lists from zwave value items."""
|
|
||||||
assert device.fan_modes == [3, 4, 5]
|
|
||||||
assert device.hvac_modes == [
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_HEAT_COOL,
|
|
||||||
]
|
|
||||||
assert device.preset_modes == []
|
|
||||||
device.values.primary = None
|
|
||||||
assert device.preset_modes == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_data_lists_single_setpoint(device_single_setpoint):
|
|
||||||
"""Test data lists from zwave value items."""
|
|
||||||
device = device_single_setpoint
|
|
||||||
assert device.fan_modes == [3, 4, 5]
|
|
||||||
assert device.hvac_modes == []
|
|
||||||
assert device.preset_modes == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_data_lists_single_setpoint_with_mode(device_single_setpoint_with_mode):
|
|
||||||
"""Test data lists from zwave value items."""
|
|
||||||
device = device_single_setpoint_with_mode
|
|
||||||
assert device.fan_modes == [3, 4, 5]
|
|
||||||
assert device.hvac_modes == [HVAC_MODE_OFF, HVAC_MODE_HEAT]
|
|
||||||
assert device.preset_modes == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_data_lists_mapping(device_mapping):
|
|
||||||
"""Test data lists from zwave value items."""
|
|
||||||
device = device_mapping
|
|
||||||
assert device.hvac_modes == ["off", "cool", "heat", "heat_cool"]
|
|
||||||
assert device.preset_modes == ["boost", "none"]
|
|
||||||
device.values.primary = None
|
|
||||||
assert device.preset_modes == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_value_set(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.values.setpoint_heating.data == 1
|
|
||||||
assert device.values.setpoint_cooling.data == 10
|
|
||||||
device.set_temperature()
|
|
||||||
assert device.values.setpoint_heating.data == 1
|
|
||||||
assert device.values.setpoint_cooling.data == 10
|
|
||||||
device.set_temperature(**{ATTR_TEMPERATURE: 2})
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_cooling.data == 10
|
|
||||||
device.set_hvac_mode(HVAC_MODE_COOL)
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_cooling.data == 10
|
|
||||||
device.set_temperature(**{ATTR_TEMPERATURE: 9})
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_cooling.data == 9
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_value_set_range(device_heat_cool_range):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_heat_cool_range
|
|
||||||
assert device.values.setpoint_heating.data == 1
|
|
||||||
assert device.values.setpoint_cooling.data == 10
|
|
||||||
device.set_temperature()
|
|
||||||
assert device.values.setpoint_heating.data == 1
|
|
||||||
assert device.values.setpoint_cooling.data == 10
|
|
||||||
device.set_temperature(**{ATTR_TARGET_TEMP_LOW: 2})
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_cooling.data == 10
|
|
||||||
device.set_temperature(**{ATTR_TARGET_TEMP_HIGH: 9})
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_cooling.data == 9
|
|
||||||
device.set_temperature(**{ATTR_TARGET_TEMP_LOW: 3, ATTR_TARGET_TEMP_HIGH: 8})
|
|
||||||
assert device.values.setpoint_heating.data == 3
|
|
||||||
assert device.values.setpoint_cooling.data == 8
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_value_set_range_away(device_heat_cool_away):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_heat_cool_away
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_cooling.data == 9
|
|
||||||
assert device.values.setpoint_away_heating.data == 1
|
|
||||||
assert device.values.setpoint_away_cooling.data == 10
|
|
||||||
device.set_preset_mode(PRESET_AWAY)
|
|
||||||
device.set_temperature(**{ATTR_TARGET_TEMP_LOW: 0, ATTR_TARGET_TEMP_HIGH: 11})
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_cooling.data == 9
|
|
||||||
assert device.values.setpoint_away_heating.data == 0
|
|
||||||
assert device.values.setpoint_away_cooling.data == 11
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_value_set_eco(device_heat_eco):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_heat_eco
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_eco_heating.data == 1
|
|
||||||
device.set_preset_mode("heat econ")
|
|
||||||
device.set_temperature(**{ATTR_TEMPERATURE: 0})
|
|
||||||
assert device.values.setpoint_heating.data == 2
|
|
||||||
assert device.values.setpoint_eco_heating.data == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_value_set_single_setpoint(device_single_setpoint):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_single_setpoint
|
|
||||||
assert device.values.primary.data == 1
|
|
||||||
device.set_temperature(**{ATTR_TEMPERATURE: 2})
|
|
||||||
assert device.values.primary.data == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_set(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
device.set_hvac_mode(HVAC_MODE_COOL)
|
|
||||||
assert device.values.primary.data == HVAC_MODE_COOL
|
|
||||||
device.set_preset_mode(PRESET_ECO)
|
|
||||||
assert device.values.primary.data == PRESET_ECO
|
|
||||||
device.set_preset_mode(PRESET_NONE)
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT_COOL
|
|
||||||
device.values.primary = None
|
|
||||||
device.set_hvac_mode("test_set_failes")
|
|
||||||
assert device.values.primary is None
|
|
||||||
device.set_preset_mode("test_set_failes")
|
|
||||||
assert device.values.primary is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_set_mapping(device_mapping):
|
|
||||||
"""Test values changed for climate device. Mapping."""
|
|
||||||
device = device_mapping
|
|
||||||
assert device.values.primary.data == "Heat"
|
|
||||||
device.set_hvac_mode(HVAC_MODE_COOL)
|
|
||||||
assert device.values.primary.data == "Cool"
|
|
||||||
device.set_hvac_mode(HVAC_MODE_OFF)
|
|
||||||
assert device.values.primary.data == "Off"
|
|
||||||
device.set_preset_mode(PRESET_BOOST)
|
|
||||||
assert device.values.primary.data == "Full Power"
|
|
||||||
device.set_preset_mode(PRESET_ECO)
|
|
||||||
assert device.values.primary.data == "eco"
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_set_unknown(device_unknown):
|
|
||||||
"""Test values changed for climate device. Unknown."""
|
|
||||||
device = device_unknown
|
|
||||||
assert device.values.primary.data == "Heat"
|
|
||||||
device.set_preset_mode("Abcdefg")
|
|
||||||
assert device.values.primary.data == "Abcdefg"
|
|
||||||
device.set_preset_mode(PRESET_NONE)
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT_COOL
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_set_heat_cool(device_heat_cool):
|
|
||||||
"""Test values changed for climate device. Heat/Cool only."""
|
|
||||||
device = device_heat_cool
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
device.set_preset_mode("Heat Eco")
|
|
||||||
assert device.values.primary.data == "Heat Eco"
|
|
||||||
device.set_preset_mode(PRESET_NONE)
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
device.set_preset_mode("Cool Eco")
|
|
||||||
assert device.values.primary.data == "Cool Eco"
|
|
||||||
device.set_preset_mode(PRESET_NONE)
|
|
||||||
assert device.values.primary.data == HVAC_MODE_COOL
|
|
||||||
|
|
||||||
|
|
||||||
def test_fan_mode_value_set(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.values.fan_mode.data == "test2"
|
|
||||||
device.set_fan_mode("test_fan_set")
|
|
||||||
assert device.values.fan_mode.data == "test_fan_set"
|
|
||||||
device.values.fan_mode = None
|
|
||||||
device.set_fan_mode("test_fan_set_failes")
|
|
||||||
assert device.values.fan_mode is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_value_changed(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.target_temperature == 1
|
|
||||||
device.values.setpoint_heating.data = 2
|
|
||||||
value_changed(device.values.setpoint_heating)
|
|
||||||
assert device.target_temperature == 2
|
|
||||||
device.values.primary.data = HVAC_MODE_COOL
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature == 10
|
|
||||||
device.values.setpoint_cooling.data = 9
|
|
||||||
value_changed(device.values.setpoint_cooling)
|
|
||||||
assert device.target_temperature == 9
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_range_changed(device_heat_cool_range):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_heat_cool_range
|
|
||||||
assert device.target_temperature_low == 1
|
|
||||||
assert device.target_temperature_high == 10
|
|
||||||
device.values.setpoint_heating.data = 2
|
|
||||||
value_changed(device.values.setpoint_heating)
|
|
||||||
assert device.target_temperature_low == 2
|
|
||||||
assert device.target_temperature_high == 10
|
|
||||||
device.values.setpoint_cooling.data = 9
|
|
||||||
value_changed(device.values.setpoint_cooling)
|
|
||||||
assert device.target_temperature_low == 2
|
|
||||||
assert device.target_temperature_high == 9
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_changed_preset_range(device_heat_cool_away):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_heat_cool_away
|
|
||||||
assert device.target_temperature_low == 2
|
|
||||||
assert device.target_temperature_high == 9
|
|
||||||
device.values.primary.data = PRESET_AWAY
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature_low == 1
|
|
||||||
assert device.target_temperature_high == 10
|
|
||||||
device.values.setpoint_away_heating.data = 0
|
|
||||||
value_changed(device.values.setpoint_away_heating)
|
|
||||||
device.values.setpoint_away_cooling.data = 11
|
|
||||||
value_changed(device.values.setpoint_away_cooling)
|
|
||||||
assert device.target_temperature_low == 0
|
|
||||||
assert device.target_temperature_high == 11
|
|
||||||
device.values.primary.data = HVAC_MODE_HEAT_COOL
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature_low == 2
|
|
||||||
assert device.target_temperature_high == 9
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_changed_eco(device_heat_eco):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_heat_eco
|
|
||||||
assert device.target_temperature == 2
|
|
||||||
device.values.primary.data = "heat econ"
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature == 1
|
|
||||||
device.values.setpoint_eco_heating.data = 0
|
|
||||||
value_changed(device.values.setpoint_eco_heating)
|
|
||||||
assert device.target_temperature == 0
|
|
||||||
device.values.primary.data = HVAC_MODE_HEAT
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_changed_with_mode(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.target_temperature == 1
|
|
||||||
device.values.primary.data = HVAC_MODE_COOL
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature == 10
|
|
||||||
device.values.primary.data = HVAC_MODE_HEAT_COOL
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature_low == 1
|
|
||||||
assert device.target_temperature_high == 10
|
|
||||||
|
|
||||||
|
|
||||||
def test_target_value_changed_single_setpoint(device_single_setpoint):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_single_setpoint
|
|
||||||
assert device.target_temperature == 1
|
|
||||||
device.values.primary.data = 2
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.target_temperature == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_temperature_value_changed(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.current_temperature == 5
|
|
||||||
device.values.temperature.data = 3
|
|
||||||
value_changed(device.values.temperature)
|
|
||||||
assert device.current_temperature == 3
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_changed(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = HVAC_MODE_COOL
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_COOL
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = HVAC_MODE_OFF
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_OFF
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary = None
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT_COOL
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_changed_preset(device_mapping):
|
|
||||||
"""Test preset changed for climate device."""
|
|
||||||
device = device_mapping
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = PRESET_ECO
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT_COOL
|
|
||||||
assert device.preset_mode == PRESET_ECO
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_changed_mapping(device_mapping):
|
|
||||||
"""Test values changed for climate device. Mapping."""
|
|
||||||
device = device_mapping
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = "Off"
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_OFF
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = "Cool"
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_COOL
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_changed_mapping_preset(device_mapping):
|
|
||||||
"""Test values changed for climate device. Mapping with presets."""
|
|
||||||
device = device_mapping
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = "Full Power"
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT_COOL
|
|
||||||
assert device.preset_mode == PRESET_BOOST
|
|
||||||
device.values.primary = None
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT_COOL
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_changed_unknown(device_unknown):
|
|
||||||
"""Test preset changed for climate device. Unknown."""
|
|
||||||
device = device_unknown
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = "Abcdefg"
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT_COOL
|
|
||||||
assert device.preset_mode == "Abcdefg"
|
|
||||||
|
|
||||||
|
|
||||||
def test_operation_value_changed_heat_cool(device_heat_cool):
|
|
||||||
"""Test preset changed for climate device. Heat/Cool only."""
|
|
||||||
device = device_heat_cool
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.preset_mode == PRESET_NONE
|
|
||||||
device.values.primary.data = "Cool Eco"
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_COOL
|
|
||||||
assert device.preset_mode == "Cool Eco"
|
|
||||||
device.values.primary.data = "Heat Eco"
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.hvac_mode == HVAC_MODE_HEAT
|
|
||||||
assert device.preset_mode == "Heat Eco"
|
|
||||||
|
|
||||||
|
|
||||||
def test_fan_mode_value_changed(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.fan_mode == "test2"
|
|
||||||
device.values.fan_mode.data = "test_updated_fan"
|
|
||||||
value_changed(device.values.fan_mode)
|
|
||||||
assert device.fan_mode == "test_updated_fan"
|
|
||||||
|
|
||||||
|
|
||||||
def test_hvac_action_value_changed(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.hvac_action == CURRENT_HVAC_HEAT
|
|
||||||
device.values.operating_state.data = CURRENT_HVAC_COOL
|
|
||||||
value_changed(device.values.operating_state)
|
|
||||||
assert device.hvac_action == CURRENT_HVAC_COOL
|
|
||||||
|
|
||||||
|
|
||||||
def test_hvac_action_value_changed_mapping(device_mapping):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_mapping
|
|
||||||
assert device.hvac_action == CURRENT_HVAC_HEAT
|
|
||||||
device.values.operating_state.data = "cooling"
|
|
||||||
value_changed(device.values.operating_state)
|
|
||||||
assert device.hvac_action == CURRENT_HVAC_COOL
|
|
||||||
|
|
||||||
|
|
||||||
def test_hvac_action_value_changed_unknown(device_unknown):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
device = device_unknown
|
|
||||||
assert device.hvac_action == "test4"
|
|
||||||
device.values.operating_state.data = "another_hvac_action"
|
|
||||||
value_changed(device.values.operating_state)
|
|
||||||
assert device.hvac_action == "another_hvac_action"
|
|
||||||
|
|
||||||
|
|
||||||
def test_fan_action_value_changed(device):
|
|
||||||
"""Test values changed for climate device."""
|
|
||||||
assert device.extra_state_attributes[climate.ATTR_FAN_ACTION] == 7
|
|
||||||
device.values.fan_action.data = 9
|
|
||||||
value_changed(device.values.fan_action)
|
|
||||||
assert device.extra_state_attributes[climate.ATTR_FAN_ACTION] == 9
|
|
||||||
|
|
||||||
|
|
||||||
def test_aux_heat_unsupported_set(device):
|
|
||||||
"""Test aux heat for climate device."""
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
device.turn_aux_heat_on()
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
device.turn_aux_heat_off()
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
|
|
||||||
|
|
||||||
def test_aux_heat_unsupported_value_changed(device):
|
|
||||||
"""Test aux heat for climate device."""
|
|
||||||
assert device.is_aux_heat is None
|
|
||||||
device.values.primary.data = HVAC_MODE_HEAT
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.is_aux_heat is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_aux_heat_set(device_aux_heat):
|
|
||||||
"""Test aux heat for climate device."""
|
|
||||||
device = device_aux_heat
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
device.turn_aux_heat_on()
|
|
||||||
assert device.values.primary.data == AUX_HEAT_ZWAVE_MODE
|
|
||||||
device.turn_aux_heat_off()
|
|
||||||
assert device.values.primary.data == HVAC_MODE_HEAT
|
|
||||||
|
|
||||||
|
|
||||||
def test_aux_heat_value_changed(device_aux_heat):
|
|
||||||
"""Test aux heat for climate device."""
|
|
||||||
device = device_aux_heat
|
|
||||||
assert device.is_aux_heat is False
|
|
||||||
device.values.primary.data = AUX_HEAT_ZWAVE_MODE
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.is_aux_heat is True
|
|
||||||
device.values.primary.data = HVAC_MODE_HEAT
|
|
||||||
value_changed(device.values.primary)
|
|
||||||
assert device.is_aux_heat is False
|
|
|
@ -1,292 +0,0 @@
|
||||||
"""Test Z-Wave cover devices."""
|
|
||||||
from unittest.mock import MagicMock
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.cover import SUPPORT_CLOSE, SUPPORT_OPEN
|
|
||||||
from homeassistant.components.zwave import (
|
|
||||||
CONF_INVERT_OPENCLOSE_BUTTONS,
|
|
||||||
CONF_INVERT_PERCENT,
|
|
||||||
const,
|
|
||||||
cover,
|
|
||||||
)
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_none(hass, mock_openzwave):
|
|
||||||
"""Test device returns none."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockEntityValues(primary=value, node=node)
|
|
||||||
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
assert device is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_rollershutter(hass, mock_openzwave):
|
|
||||||
"""Test device returns rollershutter."""
|
|
||||||
hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=0, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, open=None, close=None, node=node)
|
|
||||||
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, cover.ZwaveRollershutter)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_garagedoor_switch(hass, mock_openzwave):
|
|
||||||
"""Test device returns garage door."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=False, node=node, command_class=const.COMMAND_CLASS_SWITCH_BINARY
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, node=node)
|
|
||||||
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, cover.ZwaveGarageDoorSwitch)
|
|
||||||
assert device.device_class == "garage"
|
|
||||||
assert device.supported_features == SUPPORT_OPEN | SUPPORT_CLOSE
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_garagedoor_barrier(hass, mock_openzwave):
|
|
||||||
"""Test device returns garage door."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data="Closed", node=node, command_class=const.COMMAND_CLASS_BARRIER_OPERATOR
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, node=node)
|
|
||||||
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, cover.ZwaveGarageDoorBarrier)
|
|
||||||
assert device.device_class == "garage"
|
|
||||||
assert device.supported_features == SUPPORT_OPEN | SUPPORT_CLOSE
|
|
||||||
|
|
||||||
|
|
||||||
def test_roller_no_position_workaround(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(manufacturer_id="0047", product_type="5a52")
|
|
||||||
value = MockValue(
|
|
||||||
data=45, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, open=None, close=None, node=node)
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.current_cover_position is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_roller_value_changed(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=None, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, open=None, close=None, node=node)
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.current_cover_position is None
|
|
||||||
assert device.is_closed is None
|
|
||||||
|
|
||||||
value.data = 2
|
|
||||||
value_changed(value)
|
|
||||||
|
|
||||||
assert device.current_cover_position == 0
|
|
||||||
assert device.is_closed
|
|
||||||
|
|
||||||
value.data = 35
|
|
||||||
value_changed(value)
|
|
||||||
|
|
||||||
assert device.current_cover_position == 35
|
|
||||||
assert not device.is_closed
|
|
||||||
|
|
||||||
value.data = 97
|
|
||||||
value_changed(value)
|
|
||||||
|
|
||||||
assert device.current_cover_position == 100
|
|
||||||
assert not device.is_closed
|
|
||||||
|
|
||||||
|
|
||||||
def test_roller_commands(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
mock_network = hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=50, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL
|
|
||||||
)
|
|
||||||
open_value = MockValue(data=False, node=node)
|
|
||||||
close_value = MockValue(data=False, node=node)
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=value, open=open_value, close=close_value, node=node
|
|
||||||
)
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
device.set_cover_position(position=25)
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 25
|
|
||||||
|
|
||||||
device.open_cover()
|
|
||||||
assert mock_network.manager.pressButton.called
|
|
||||||
(value_id,) = mock_network.manager.pressButton.mock_calls.pop(0)[1]
|
|
||||||
assert value_id == open_value.value_id
|
|
||||||
|
|
||||||
device.close_cover()
|
|
||||||
assert mock_network.manager.pressButton.called
|
|
||||||
(value_id,) = mock_network.manager.pressButton.mock_calls.pop(0)[1]
|
|
||||||
assert value_id == close_value.value_id
|
|
||||||
|
|
||||||
device.stop_cover()
|
|
||||||
assert mock_network.manager.releaseButton.called
|
|
||||||
(value_id,) = mock_network.manager.releaseButton.mock_calls.pop(0)[1]
|
|
||||||
assert value_id == open_value.value_id
|
|
||||||
|
|
||||||
|
|
||||||
def test_roller_invert_percent(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
mock_network = hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=50, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL
|
|
||||||
)
|
|
||||||
open_value = MockValue(data=False, node=node)
|
|
||||||
close_value = MockValue(data=False, node=node)
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=value, open=open_value, close=close_value, node=node
|
|
||||||
)
|
|
||||||
device = cover.get_device(
|
|
||||||
hass=hass, node=node, values=values, node_config={CONF_INVERT_PERCENT: True}
|
|
||||||
)
|
|
||||||
|
|
||||||
device.set_cover_position(position=25)
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 75
|
|
||||||
|
|
||||||
device.open_cover()
|
|
||||||
assert mock_network.manager.pressButton.called
|
|
||||||
(value_id,) = mock_network.manager.pressButton.mock_calls.pop(0)[1]
|
|
||||||
assert value_id == open_value.value_id
|
|
||||||
|
|
||||||
|
|
||||||
def test_roller_reverse_open_close(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
mock_network = hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=50, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL
|
|
||||||
)
|
|
||||||
open_value = MockValue(data=False, node=node)
|
|
||||||
close_value = MockValue(data=False, node=node)
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=value, open=open_value, close=close_value, node=node
|
|
||||||
)
|
|
||||||
device = cover.get_device(
|
|
||||||
hass=hass,
|
|
||||||
node=node,
|
|
||||||
values=values,
|
|
||||||
node_config={CONF_INVERT_OPENCLOSE_BUTTONS: True},
|
|
||||||
)
|
|
||||||
|
|
||||||
device.open_cover()
|
|
||||||
assert mock_network.manager.pressButton.called
|
|
||||||
(value_id,) = mock_network.manager.pressButton.mock_calls.pop(0)[1]
|
|
||||||
assert value_id == close_value.value_id
|
|
||||||
|
|
||||||
device.close_cover()
|
|
||||||
assert mock_network.manager.pressButton.called
|
|
||||||
(value_id,) = mock_network.manager.pressButton.mock_calls.pop(0)[1]
|
|
||||||
assert value_id == open_value.value_id
|
|
||||||
|
|
||||||
device.stop_cover()
|
|
||||||
assert mock_network.manager.releaseButton.called
|
|
||||||
(value_id,) = mock_network.manager.releaseButton.mock_calls.pop(0)[1]
|
|
||||||
assert value_id == close_value.value_id
|
|
||||||
|
|
||||||
|
|
||||||
def test_switch_garage_value_changed(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=False, node=node, command_class=const.COMMAND_CLASS_SWITCH_BINARY
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, node=node)
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.is_closed
|
|
||||||
|
|
||||||
value.data = True
|
|
||||||
value_changed(value)
|
|
||||||
assert not device.is_closed
|
|
||||||
|
|
||||||
|
|
||||||
def test_switch_garage_commands(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data=False, node=node, command_class=const.COMMAND_CLASS_SWITCH_BINARY
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, node=node)
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert value.data is False
|
|
||||||
device.open_cover()
|
|
||||||
assert value.data is True
|
|
||||||
device.close_cover()
|
|
||||||
assert value.data is False
|
|
||||||
|
|
||||||
|
|
||||||
def test_barrier_garage_value_changed(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data="Closed", node=node, command_class=const.COMMAND_CLASS_BARRIER_OPERATOR
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, node=node)
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.is_closed
|
|
||||||
assert not device.is_opening
|
|
||||||
assert not device.is_closing
|
|
||||||
|
|
||||||
value.data = "Opening"
|
|
||||||
value_changed(value)
|
|
||||||
assert not device.is_closed
|
|
||||||
assert device.is_opening
|
|
||||||
assert not device.is_closing
|
|
||||||
|
|
||||||
value.data = "Opened"
|
|
||||||
value_changed(value)
|
|
||||||
assert not device.is_closed
|
|
||||||
assert not device.is_opening
|
|
||||||
assert not device.is_closing
|
|
||||||
|
|
||||||
value.data = "Closing"
|
|
||||||
value_changed(value)
|
|
||||||
assert not device.is_closed
|
|
||||||
assert not device.is_opening
|
|
||||||
assert device.is_closing
|
|
||||||
|
|
||||||
|
|
||||||
def test_barrier_garage_commands(hass, mock_openzwave):
|
|
||||||
"""Test position changed."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(
|
|
||||||
data="Closed", node=node, command_class=const.COMMAND_CLASS_BARRIER_OPERATOR
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value, node=node)
|
|
||||||
device = cover.get_device(hass=hass, node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert value.data == "Closed"
|
|
||||||
device.open_cover()
|
|
||||||
assert value.data == "Opened"
|
|
||||||
device.close_cover()
|
|
||||||
assert value.data == "Closed"
|
|
|
@ -1,91 +0,0 @@
|
||||||
"""Test Z-Wave fans."""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.fan import SUPPORT_SET_SPEED
|
|
||||||
from homeassistant.components.zwave import fan
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_fan(mock_openzwave):
|
|
||||||
"""Test get_device returns a zwave fan."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = fan.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, fan.ZwaveFan)
|
|
||||||
assert device.supported_features == SUPPORT_SET_SPEED
|
|
||||||
|
|
||||||
|
|
||||||
def test_fan_turn_on(mock_openzwave):
|
|
||||||
"""Test turning on a zwave fan."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
device = fan.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
device.turn_on()
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 255
|
|
||||||
|
|
||||||
node.reset_mock()
|
|
||||||
|
|
||||||
device.turn_on(percentage=0)
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 0
|
|
||||||
|
|
||||||
node.reset_mock()
|
|
||||||
|
|
||||||
device.turn_on(percentage=1)
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 1
|
|
||||||
|
|
||||||
node.reset_mock()
|
|
||||||
|
|
||||||
device.turn_on(percentage=50)
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 50
|
|
||||||
|
|
||||||
node.reset_mock()
|
|
||||||
|
|
||||||
device.turn_on(percentage=100)
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 99
|
|
||||||
|
|
||||||
|
|
||||||
def test_fan_turn_off(mock_openzwave):
|
|
||||||
"""Test turning off a dimmable zwave fan."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=46, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
device = fan.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
device.turn_off()
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 0
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,481 +0,0 @@
|
||||||
"""Test Z-Wave lights."""
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components import zwave
|
|
||||||
from homeassistant.components.light import (
|
|
||||||
ATTR_BRIGHTNESS,
|
|
||||||
ATTR_COLOR_TEMP,
|
|
||||||
ATTR_RGB_COLOR,
|
|
||||||
ATTR_RGBW_COLOR,
|
|
||||||
ATTR_TRANSITION,
|
|
||||||
COLOR_MODE_BRIGHTNESS,
|
|
||||||
COLOR_MODE_COLOR_TEMP,
|
|
||||||
COLOR_MODE_RGB,
|
|
||||||
COLOR_MODE_RGBW,
|
|
||||||
SUPPORT_TRANSITION,
|
|
||||||
)
|
|
||||||
from homeassistant.components.zwave import const, light
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
class MockLightValues(MockEntityValues):
|
|
||||||
"""Mock Z-Wave light values."""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
"""Initialize the mock zwave values."""
|
|
||||||
self.dimming_duration = None
|
|
||||||
self.color = None
|
|
||||||
self.color_channels = None
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_dimmer(mock_openzwave):
|
|
||||||
"""Test get_device returns a normal dimmer."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, light.ZwaveDimmer)
|
|
||||||
assert device.color_mode == COLOR_MODE_BRIGHTNESS
|
|
||||||
assert device.supported_features == 0
|
|
||||||
assert device.supported_color_modes == {COLOR_MODE_BRIGHTNESS}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_colorlight(mock_openzwave):
|
|
||||||
"""Test get_device returns a color light."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, light.ZwaveColorLight)
|
|
||||||
assert device.color_mode == COLOR_MODE_RGB
|
|
||||||
assert device.supported_features == 0
|
|
||||||
assert device.supported_color_modes == {COLOR_MODE_RGB}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_zw098(mock_openzwave):
|
|
||||||
"""Test get_device returns a zw098 color light."""
|
|
||||||
node = MockNode(
|
|
||||||
manufacturer_id="0086",
|
|
||||||
product_id="0062",
|
|
||||||
command_classes=[const.COMMAND_CLASS_SWITCH_COLOR],
|
|
||||||
)
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, light.ZwaveColorLight)
|
|
||||||
assert device.color_mode == COLOR_MODE_RGB
|
|
||||||
assert device.supported_features == 0
|
|
||||||
assert device.supported_color_modes == {COLOR_MODE_COLOR_TEMP, COLOR_MODE_RGB}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_rgbw_light(mock_openzwave):
|
|
||||||
"""Test get_device returns a color light."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
color_channels = MockValue(data=0x1D, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
device.value_added()
|
|
||||||
assert isinstance(device, light.ZwaveColorLight)
|
|
||||||
assert device.color_mode == COLOR_MODE_RGBW
|
|
||||||
assert device.supported_features == 0
|
|
||||||
assert device.supported_color_modes == {COLOR_MODE_RGBW}
|
|
||||||
|
|
||||||
|
|
||||||
def test_dimmer_turn_on(mock_openzwave):
|
|
||||||
"""Test turning on a dimmable Z-Wave light."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
device.turn_on()
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 255
|
|
||||||
|
|
||||||
node.reset_mock()
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_BRIGHTNESS: 224})
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 87 # round(224 / 255 * 99)
|
|
||||||
|
|
||||||
node.reset_mock()
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_BRIGHTNESS: 120})
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 47 # round(120 / 255 * 99)
|
|
||||||
|
|
||||||
with patch.object(light, "_LOGGER", MagicMock()) as mock_logger:
|
|
||||||
device.turn_on(**{ATTR_TRANSITION: 35})
|
|
||||||
assert mock_logger.debug.called
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
msg, entity_id = mock_logger.debug.mock_calls[0][1]
|
|
||||||
assert entity_id == device.entity_id
|
|
||||||
|
|
||||||
|
|
||||||
def test_dimmer_min_brightness(mock_openzwave):
|
|
||||||
"""Test turning on a dimmable Z-Wave light to its minimum brightness."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert not device.is_on
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_BRIGHTNESS: 1})
|
|
||||||
|
|
||||||
assert device.is_on
|
|
||||||
assert device.brightness == 1
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_BRIGHTNESS: 0})
|
|
||||||
|
|
||||||
assert device.is_on
|
|
||||||
assert device.brightness == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_dimmer_transitions(mock_openzwave):
|
|
||||||
"""Test dimming transition on a dimmable Z-Wave light."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
duration = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value, dimming_duration=duration)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
assert device.color_mode == COLOR_MODE_BRIGHTNESS
|
|
||||||
assert device.supported_features == SUPPORT_TRANSITION
|
|
||||||
assert device.supported_color_modes == {COLOR_MODE_BRIGHTNESS}
|
|
||||||
|
|
||||||
# Test turn_on
|
|
||||||
# Factory Default
|
|
||||||
device.turn_on()
|
|
||||||
assert duration.data == 0xFF
|
|
||||||
|
|
||||||
# Seconds transition
|
|
||||||
device.turn_on(**{ATTR_TRANSITION: 45})
|
|
||||||
assert duration.data == 45
|
|
||||||
|
|
||||||
# Minutes transition
|
|
||||||
device.turn_on(**{ATTR_TRANSITION: 245})
|
|
||||||
assert duration.data == 0x83
|
|
||||||
|
|
||||||
# Clipped transition
|
|
||||||
device.turn_on(**{ATTR_TRANSITION: 10000})
|
|
||||||
assert duration.data == 0xFE
|
|
||||||
|
|
||||||
# Test turn_off
|
|
||||||
# Factory Default
|
|
||||||
device.turn_off()
|
|
||||||
assert duration.data == 0xFF
|
|
||||||
|
|
||||||
# Seconds transition
|
|
||||||
device.turn_off(**{ATTR_TRANSITION: 45})
|
|
||||||
assert duration.data == 45
|
|
||||||
|
|
||||||
# Minutes transition
|
|
||||||
device.turn_off(**{ATTR_TRANSITION: 245})
|
|
||||||
assert duration.data == 0x83
|
|
||||||
|
|
||||||
# Clipped transition
|
|
||||||
device.turn_off(**{ATTR_TRANSITION: 10000})
|
|
||||||
assert duration.data == 0xFE
|
|
||||||
|
|
||||||
|
|
||||||
def test_dimmer_turn_off(mock_openzwave):
|
|
||||||
"""Test turning off a dimmable Z-Wave light."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=46, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
device.turn_off()
|
|
||||||
|
|
||||||
assert node.set_dimmer.called
|
|
||||||
value_id, brightness = node.set_dimmer.mock_calls[0][1]
|
|
||||||
assert value_id == value.value_id
|
|
||||||
assert brightness == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_dimmer_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for dimmer lights."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert not device.is_on
|
|
||||||
|
|
||||||
value.data = 46
|
|
||||||
value_changed(value)
|
|
||||||
|
|
||||||
assert device.is_on
|
|
||||||
assert device.brightness == 118
|
|
||||||
|
|
||||||
|
|
||||||
def test_dimmer_refresh_value(mock_openzwave):
|
|
||||||
"""Test value changed for dimmer lights."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
device = light.get_device(
|
|
||||||
node=node,
|
|
||||||
values=values,
|
|
||||||
node_config={zwave.CONF_REFRESH_VALUE: True, zwave.CONF_REFRESH_DELAY: 5},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert not device.is_on
|
|
||||||
|
|
||||||
with patch.object(light, "Timer") as mock_timer:
|
|
||||||
value.data = 46
|
|
||||||
value_changed(value)
|
|
||||||
|
|
||||||
assert not device.is_on
|
|
||||||
assert mock_timer.called
|
|
||||||
assert len(mock_timer.mock_calls) == 2
|
|
||||||
timeout, callback = mock_timer.mock_calls[0][1][:2]
|
|
||||||
assert timeout == 5
|
|
||||||
assert mock_timer().start.called
|
|
||||||
assert len(mock_timer().start.mock_calls) == 1
|
|
||||||
|
|
||||||
with patch.object(light, "Timer") as mock_timer_2:
|
|
||||||
value_changed(value)
|
|
||||||
assert not device.is_on
|
|
||||||
assert mock_timer().cancel.called
|
|
||||||
assert len(mock_timer_2.mock_calls) == 2
|
|
||||||
timeout, callback = mock_timer_2.mock_calls[0][1][:2]
|
|
||||||
assert timeout == 5
|
|
||||||
assert mock_timer_2().start.called
|
|
||||||
assert len(mock_timer_2().start.mock_calls) == 1
|
|
||||||
|
|
||||||
callback()
|
|
||||||
assert device.is_on
|
|
||||||
assert device.brightness == 118
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_rgb_color(mock_openzwave):
|
|
||||||
"""Test setting zwave light color."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGB only
|
|
||||||
color_channels = MockValue(data=0x1C, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert color.data == "#0000000000"
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_RGB_COLOR: (0xFF, 0xBF, 0x7F)})
|
|
||||||
|
|
||||||
assert color.data == "#ffbf7f0000"
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_white_value(mock_openzwave):
|
|
||||||
"""Test setting zwave light color."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGBW
|
|
||||||
color_channels = MockValue(data=0x1D, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert color.data == "#0000000000"
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_RGBW_COLOR: (0xFF, 0xFF, 0xFF, 0xC8)})
|
|
||||||
|
|
||||||
assert color.data == "#ffffffc800"
|
|
||||||
|
|
||||||
|
|
||||||
def test_disable_white_if_set_color(mock_openzwave):
|
|
||||||
"""
|
|
||||||
Test that _white is set to 0 if turn_on with ATTR_RGB_COLOR.
|
|
||||||
|
|
||||||
See Issue #13930 - many RGBW ZWave bulbs will only activate the RGB LED to
|
|
||||||
produce color if _white is set to zero.
|
|
||||||
"""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGB only
|
|
||||||
color_channels = MockValue(data=0x1C, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
device._white = 234
|
|
||||||
|
|
||||||
assert color.data == "#0000000000"
|
|
||||||
assert device.rgbw_color == (0, 0, 0, 234)
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_RGB_COLOR: (0xFF, 0xBF, 0x7F)})
|
|
||||||
|
|
||||||
assert color.data == "#ffbf7f0000"
|
|
||||||
assert device.rgbw_color == (0xFF, 0xBF, 0x7F, 0x00)
|
|
||||||
|
|
||||||
|
|
||||||
def test_zw098_set_color_temp(mock_openzwave):
|
|
||||||
"""Test setting zwave light color."""
|
|
||||||
node = MockNode(
|
|
||||||
manufacturer_id="0086",
|
|
||||||
product_id="0062",
|
|
||||||
command_classes=[const.COMMAND_CLASS_SWITCH_COLOR],
|
|
||||||
)
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGB, warm white, cold white
|
|
||||||
color_channels = MockValue(data=0x1F, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert color.data == "#0000000000"
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_COLOR_TEMP: 200})
|
|
||||||
|
|
||||||
assert color.data == "#00000000ff"
|
|
||||||
|
|
||||||
device.turn_on(**{ATTR_COLOR_TEMP: 400})
|
|
||||||
|
|
||||||
assert color.data == "#000000ff00"
|
|
||||||
|
|
||||||
|
|
||||||
def test_rgb_not_supported(mock_openzwave):
|
|
||||||
"""Test value changed for rgb lights."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports color temperature only
|
|
||||||
color_channels = MockValue(data=0x01, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.rgb_color is None
|
|
||||||
assert device.rgbw_color is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_color_value(mock_openzwave):
|
|
||||||
"""Test value changed for rgb lights."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockLightValues(primary=value)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.rgb_color is None
|
|
||||||
assert device.rgbw_color is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_color_channels_value(mock_openzwave):
|
|
||||||
"""Test value changed for rgb lights."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.rgb_color is None
|
|
||||||
assert device.rgbw_color is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_rgb_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for rgb lights."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGB only
|
|
||||||
color_channels = MockValue(data=0x1C, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.rgb_color == (0, 0, 0)
|
|
||||||
|
|
||||||
color.data = "#ffbf800000"
|
|
||||||
value_changed(color)
|
|
||||||
|
|
||||||
assert device.rgb_color == (0xFF, 0xBF, 0x80)
|
|
||||||
|
|
||||||
|
|
||||||
def test_rgbww_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for rgb lights."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGB, Warm White
|
|
||||||
color_channels = MockValue(data=0x1D, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.rgbw_color == (0, 0, 0, 0)
|
|
||||||
|
|
||||||
color.data = "#c86400c800"
|
|
||||||
value_changed(color)
|
|
||||||
|
|
||||||
assert device.rgbw_color == (0xC8, 0x64, 0x00, 0xC8)
|
|
||||||
|
|
||||||
|
|
||||||
def test_rgbcw_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for rgb lights."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR])
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGB, Cold White
|
|
||||||
color_channels = MockValue(data=0x1E, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.rgbw_color == (0, 0, 0, 0)
|
|
||||||
|
|
||||||
color.data = "#c86400c800"
|
|
||||||
value_changed(color)
|
|
||||||
|
|
||||||
assert device.rgbw_color == (0xC8, 0x64, 0x00, 0xC8)
|
|
||||||
|
|
||||||
|
|
||||||
def test_ct_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for zw098 lights."""
|
|
||||||
node = MockNode(
|
|
||||||
manufacturer_id="0086",
|
|
||||||
product_id="0062",
|
|
||||||
command_classes=[const.COMMAND_CLASS_SWITCH_COLOR],
|
|
||||||
)
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
color = MockValue(data="#0000000000", node=node)
|
|
||||||
# Supports RGB, Cold White
|
|
||||||
color_channels = MockValue(data=0x1F, node=node)
|
|
||||||
values = MockLightValues(primary=value, color=color, color_channels=color_channels)
|
|
||||||
device = light.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.color_mode == COLOR_MODE_RGB
|
|
||||||
assert device.color_temp is None
|
|
||||||
|
|
||||||
color.data = "#000000ff00"
|
|
||||||
value_changed(color)
|
|
||||||
|
|
||||||
assert device.color_mode == COLOR_MODE_COLOR_TEMP
|
|
||||||
assert device.color_temp == light.TEMP_WARM_HASS
|
|
||||||
|
|
||||||
color.data = "#00000000ff"
|
|
||||||
value_changed(color)
|
|
||||||
|
|
||||||
assert device.color_mode == COLOR_MODE_COLOR_TEMP
|
|
||||||
assert device.color_temp == light.TEMP_COLD_HASS
|
|
||||||
|
|
||||||
color.data = "#ff00000000"
|
|
||||||
value_changed(color)
|
|
||||||
assert device.color_mode == COLOR_MODE_RGB
|
|
|
@ -1,389 +0,0 @@
|
||||||
"""Test Z-Wave locks."""
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.components.zwave import const, lock
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_lock(mock_openzwave):
|
|
||||||
"""Test get_device returns a Z-Wave lock."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=None, node=node),
|
|
||||||
access_control=None,
|
|
||||||
alarm_type=None,
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
device = lock.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, lock.ZwaveLock)
|
|
||||||
|
|
||||||
|
|
||||||
def test_lock_turn_on_and_off(mock_openzwave):
|
|
||||||
"""Test turning on a Z-Wave lock."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=None, node=node),
|
|
||||||
access_control=None,
|
|
||||||
alarm_type=None,
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert not values.primary.data
|
|
||||||
|
|
||||||
device.lock()
|
|
||||||
assert values.primary.data
|
|
||||||
|
|
||||||
device.unlock()
|
|
||||||
assert not values.primary.data
|
|
||||||
|
|
||||||
|
|
||||||
def test_lock_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave lock."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=None, node=node),
|
|
||||||
access_control=None,
|
|
||||||
alarm_type=None,
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert not device.is_locked
|
|
||||||
|
|
||||||
values.primary.data = True
|
|
||||||
value_changed(values.primary)
|
|
||||||
|
|
||||||
assert device.is_locked
|
|
||||||
|
|
||||||
|
|
||||||
def test_lock_state_workaround(mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave lock using notification state."""
|
|
||||||
node = MockNode(manufacturer_id="0090", product_id="0440")
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=True, node=node),
|
|
||||||
access_control=MockValue(data=1, node=node),
|
|
||||||
alarm_type=None,
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values)
|
|
||||||
assert device.is_locked
|
|
||||||
values.access_control.data = 2
|
|
||||||
value_changed(values.access_control)
|
|
||||||
assert not device.is_locked
|
|
||||||
|
|
||||||
|
|
||||||
def test_track_message_workaround(mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave lock by alarm-clearing workaround."""
|
|
||||||
node = MockNode(
|
|
||||||
manufacturer_id="003B",
|
|
||||||
product_id="5044",
|
|
||||||
stats={"lastReceivedMessage": [0] * 6},
|
|
||||||
)
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=True, node=node),
|
|
||||||
access_control=None,
|
|
||||||
alarm_type=None,
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Here we simulate an RF lock. The first lock.get_device will call
|
|
||||||
# update properties, simulating the first DoorLock report. We then trigger
|
|
||||||
# a change, simulating the openzwave automatic refreshing behavior (which
|
|
||||||
# is enabled for at least the lock that needs this workaround)
|
|
||||||
node.stats["lastReceivedMessage"][5] = const.COMMAND_CLASS_DOOR_LOCK
|
|
||||||
device = lock.get_device(node=node, values=values)
|
|
||||||
value_changed(values.primary)
|
|
||||||
assert device.is_locked
|
|
||||||
assert device.extra_state_attributes[lock.ATTR_NOTIFICATION] == "RF Lock"
|
|
||||||
|
|
||||||
# Simulate a keypad unlock. We trigger a value_changed() which simulates
|
|
||||||
# the Alarm notification received from the lock. Then, we trigger
|
|
||||||
# value_changed() to simulate the automatic refreshing behavior.
|
|
||||||
values.access_control = MockValue(data=6, node=node)
|
|
||||||
values.alarm_type = MockValue(data=19, node=node)
|
|
||||||
values.alarm_level = MockValue(data=3, node=node)
|
|
||||||
node.stats["lastReceivedMessage"][5] = const.COMMAND_CLASS_ALARM
|
|
||||||
value_changed(values.access_control)
|
|
||||||
node.stats["lastReceivedMessage"][5] = const.COMMAND_CLASS_DOOR_LOCK
|
|
||||||
values.primary.data = False
|
|
||||||
value_changed(values.primary)
|
|
||||||
assert not device.is_locked
|
|
||||||
assert (
|
|
||||||
device.extra_state_attributes[lock.ATTR_LOCK_STATUS]
|
|
||||||
== "Unlocked with Keypad by user 3"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Again, simulate an RF lock.
|
|
||||||
device.lock()
|
|
||||||
node.stats["lastReceivedMessage"][5] = const.COMMAND_CLASS_DOOR_LOCK
|
|
||||||
value_changed(values.primary)
|
|
||||||
assert device.is_locked
|
|
||||||
assert device.extra_state_attributes[lock.ATTR_NOTIFICATION] == "RF Lock"
|
|
||||||
|
|
||||||
|
|
||||||
def test_v2btze_value_changed(mock_openzwave):
|
|
||||||
"""Test value changed for v2btze Z-Wave lock."""
|
|
||||||
node = MockNode(manufacturer_id="010e", product_id="0002")
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=None, node=node),
|
|
||||||
v2btze_advanced=MockValue(data="Advanced", node=node),
|
|
||||||
access_control=MockValue(data=19, node=node),
|
|
||||||
alarm_type=None,
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values, node_config={})
|
|
||||||
assert device._v2btze
|
|
||||||
|
|
||||||
assert not device.is_locked
|
|
||||||
|
|
||||||
values.access_control.data = 24
|
|
||||||
value_changed(values.primary)
|
|
||||||
|
|
||||||
assert device.is_locked
|
|
||||||
|
|
||||||
|
|
||||||
def test_alarm_type_workaround(mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave lock using alarm type."""
|
|
||||||
node = MockNode(manufacturer_id="0109", product_id="0000")
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=True, node=node),
|
|
||||||
access_control=None,
|
|
||||||
alarm_type=MockValue(data=16, node=node),
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values)
|
|
||||||
assert not device.is_locked
|
|
||||||
|
|
||||||
values.alarm_type.data = 18
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert device.is_locked
|
|
||||||
|
|
||||||
values.alarm_type.data = 19
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert not device.is_locked
|
|
||||||
|
|
||||||
values.alarm_type.data = 21
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert device.is_locked
|
|
||||||
|
|
||||||
values.alarm_type.data = 22
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert not device.is_locked
|
|
||||||
|
|
||||||
values.alarm_type.data = 24
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert device.is_locked
|
|
||||||
|
|
||||||
values.alarm_type.data = 25
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert not device.is_locked
|
|
||||||
|
|
||||||
values.alarm_type.data = 27
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert device.is_locked
|
|
||||||
|
|
||||||
|
|
||||||
def test_lock_access_control(mock_openzwave):
|
|
||||||
"""Test access control for Z-Wave lock."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=None, node=node),
|
|
||||||
access_control=MockValue(data=11, node=node),
|
|
||||||
alarm_type=None,
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert device.extra_state_attributes[lock.ATTR_NOTIFICATION] == "Lock Jammed"
|
|
||||||
|
|
||||||
|
|
||||||
def test_lock_alarm_type(mock_openzwave):
|
|
||||||
"""Test alarm type for Z-Wave lock."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=None, node=node),
|
|
||||||
access_control=None,
|
|
||||||
alarm_type=MockValue(data=None, node=node),
|
|
||||||
alarm_level=None,
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert lock.ATTR_LOCK_STATUS not in device.extra_state_attributes
|
|
||||||
|
|
||||||
values.alarm_type.data = 21
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert (
|
|
||||||
device.extra_state_attributes[lock.ATTR_LOCK_STATUS] == "Manually Locked None"
|
|
||||||
)
|
|
||||||
|
|
||||||
values.alarm_type.data = 18
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert (
|
|
||||||
device.extra_state_attributes[lock.ATTR_LOCK_STATUS]
|
|
||||||
== "Locked with Keypad by user None"
|
|
||||||
)
|
|
||||||
|
|
||||||
values.alarm_type.data = 161
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert device.extra_state_attributes[lock.ATTR_LOCK_STATUS] == "Tamper Alarm: None"
|
|
||||||
|
|
||||||
values.alarm_type.data = 9
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
assert device.extra_state_attributes[lock.ATTR_LOCK_STATUS] == "Deadbolt Jammed"
|
|
||||||
|
|
||||||
|
|
||||||
def test_lock_alarm_level(mock_openzwave):
|
|
||||||
"""Test alarm level for Z-Wave lock."""
|
|
||||||
node = MockNode()
|
|
||||||
values = MockEntityValues(
|
|
||||||
primary=MockValue(data=None, node=node),
|
|
||||||
access_control=None,
|
|
||||||
alarm_type=MockValue(data=None, node=node),
|
|
||||||
alarm_level=MockValue(data=None, node=node),
|
|
||||||
)
|
|
||||||
device = lock.get_device(node=node, values=values, node_config={})
|
|
||||||
|
|
||||||
assert lock.ATTR_LOCK_STATUS not in device.extra_state_attributes
|
|
||||||
|
|
||||||
values.alarm_type.data = 21
|
|
||||||
values.alarm_level.data = 1
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
value_changed(values.alarm_level)
|
|
||||||
assert (
|
|
||||||
device.extra_state_attributes[lock.ATTR_LOCK_STATUS]
|
|
||||||
== "Manually Locked by Key Cylinder or Inside thumb turn"
|
|
||||||
)
|
|
||||||
|
|
||||||
values.alarm_type.data = 18
|
|
||||||
values.alarm_level.data = "alice"
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
value_changed(values.alarm_level)
|
|
||||||
assert (
|
|
||||||
device.extra_state_attributes[lock.ATTR_LOCK_STATUS]
|
|
||||||
== "Locked with Keypad by user alice"
|
|
||||||
)
|
|
||||||
|
|
||||||
values.alarm_type.data = 161
|
|
||||||
values.alarm_level.data = 1
|
|
||||||
value_changed(values.alarm_type)
|
|
||||||
value_changed(values.alarm_level)
|
|
||||||
assert (
|
|
||||||
device.extra_state_attributes[lock.ATTR_LOCK_STATUS]
|
|
||||||
== "Tamper Alarm: Too many keypresses"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def setup_ozw(hass, mock_openzwave):
|
|
||||||
"""Set up the mock ZWave config entry."""
|
|
||||||
hass.config.components.add("zwave")
|
|
||||||
config_entry = config_entries.ConfigEntry(
|
|
||||||
1,
|
|
||||||
"zwave",
|
|
||||||
"Mock Title",
|
|
||||||
{"usb_path": "mock-path", "network_key": "mock-key"},
|
|
||||||
"test",
|
|
||||||
)
|
|
||||||
await hass.config_entries.async_forward_entry_setup(config_entry, "lock")
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_lock_set_usercode_service(hass, mock_openzwave):
|
|
||||||
"""Test the zwave lock set_usercode service."""
|
|
||||||
mock_network = hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
|
|
||||||
node = MockNode(node_id=12)
|
|
||||||
value0 = MockValue(data=" ", node=node, index=0)
|
|
||||||
value1 = MockValue(data=" ", node=node, index=1)
|
|
||||||
|
|
||||||
node.get_values.return_value = {value0.value_id: value0, value1.value_id: value1}
|
|
||||||
|
|
||||||
mock_network.nodes = {node.node_id: node}
|
|
||||||
|
|
||||||
await setup_ozw(hass, mock_openzwave)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
lock.DOMAIN,
|
|
||||||
lock.SERVICE_SET_USERCODE,
|
|
||||||
{
|
|
||||||
const.ATTR_NODE_ID: node.node_id,
|
|
||||||
lock.ATTR_USERCODE: "1234",
|
|
||||||
lock.ATTR_CODE_SLOT: 1,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert value1.data == "1234"
|
|
||||||
|
|
||||||
mock_network.nodes = {node.node_id: node}
|
|
||||||
await hass.services.async_call(
|
|
||||||
lock.DOMAIN,
|
|
||||||
lock.SERVICE_SET_USERCODE,
|
|
||||||
{
|
|
||||||
const.ATTR_NODE_ID: node.node_id,
|
|
||||||
lock.ATTR_USERCODE: "123",
|
|
||||||
lock.ATTR_CODE_SLOT: 1,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert value1.data == "1234"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_lock_get_usercode_service(hass, mock_openzwave):
|
|
||||||
"""Test the zwave lock get_usercode service."""
|
|
||||||
mock_network = hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=12)
|
|
||||||
value0 = MockValue(data=None, node=node, index=0)
|
|
||||||
value1 = MockValue(data="1234", node=node, index=1)
|
|
||||||
|
|
||||||
node.get_values.return_value = {value0.value_id: value0, value1.value_id: value1}
|
|
||||||
|
|
||||||
await setup_ozw(hass, mock_openzwave)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
with patch.object(lock, "_LOGGER") as mock_logger:
|
|
||||||
mock_network.nodes = {node.node_id: node}
|
|
||||||
await hass.services.async_call(
|
|
||||||
lock.DOMAIN,
|
|
||||||
lock.SERVICE_GET_USERCODE,
|
|
||||||
{const.ATTR_NODE_ID: node.node_id, lock.ATTR_CODE_SLOT: 1},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
# This service only seems to write to the log
|
|
||||||
assert mock_logger.info.called
|
|
||||||
assert len(mock_logger.info.mock_calls) == 1
|
|
||||||
assert mock_logger.info.mock_calls[0][1][2] == "1234"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_lock_clear_usercode_service(hass, mock_openzwave):
|
|
||||||
"""Test the zwave lock clear_usercode service."""
|
|
||||||
mock_network = hass.data[const.DATA_NETWORK] = MagicMock()
|
|
||||||
node = MockNode(node_id=12)
|
|
||||||
value0 = MockValue(data=None, node=node, index=0)
|
|
||||||
value1 = MockValue(data="123", node=node, index=1)
|
|
||||||
|
|
||||||
node.get_values.return_value = {value0.value_id: value0, value1.value_id: value1}
|
|
||||||
|
|
||||||
mock_network.nodes = {node.node_id: node}
|
|
||||||
|
|
||||||
await setup_ozw(hass, mock_openzwave)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
lock.DOMAIN,
|
|
||||||
lock.SERVICE_CLEAR_USERCODE,
|
|
||||||
{const.ATTR_NODE_ID: node.node_id, lock.ATTR_CODE_SLOT: 1},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert value1.data == "\0\0\0"
|
|
|
@ -1,723 +0,0 @@
|
||||||
"""Test Z-Wave node entity."""
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.zwave import const, node_entity
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
|
||||||
|
|
||||||
import tests.mock.zwave as mock_zwave
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_maybe_schedule_update(hass, mock_openzwave):
|
|
||||||
"""Test maybe schedule update."""
|
|
||||||
base_entity = node_entity.ZWaveBaseEntity()
|
|
||||||
base_entity.entity_id = "zwave.bla"
|
|
||||||
base_entity.hass = hass
|
|
||||||
|
|
||||||
with patch.object(hass.loop, "call_later") as mock_call_later:
|
|
||||||
base_entity._schedule_update()
|
|
||||||
assert mock_call_later.called
|
|
||||||
|
|
||||||
base_entity._schedule_update()
|
|
||||||
assert len(mock_call_later.mock_calls) == 1
|
|
||||||
assert base_entity._update_scheduled is True
|
|
||||||
|
|
||||||
do_update = mock_call_later.mock_calls[0][1][1]
|
|
||||||
|
|
||||||
do_update()
|
|
||||||
assert base_entity._update_scheduled is False
|
|
||||||
|
|
||||||
base_entity._schedule_update()
|
|
||||||
assert len(mock_call_later.mock_calls) == 2
|
|
||||||
|
|
||||||
|
|
||||||
async def test_node_event_activated(hass, mock_openzwave):
|
|
||||||
"""Test Node event activated event."""
|
|
||||||
mock_receivers = []
|
|
||||||
|
|
||||||
def mock_connect(receiver, signal, *args, **kwargs):
|
|
||||||
if signal == mock_zwave.MockNetwork.SIGNAL_NODE_EVENT:
|
|
||||||
mock_receivers.append(receiver)
|
|
||||||
|
|
||||||
node = mock_zwave.MockNode(node_id=11)
|
|
||||||
|
|
||||||
with patch("pydispatch.dispatcher.connect", new=mock_connect):
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, mock_openzwave)
|
|
||||||
|
|
||||||
assert len(mock_receivers) == 1
|
|
||||||
|
|
||||||
events = []
|
|
||||||
|
|
||||||
def listener(event):
|
|
||||||
events.append(event)
|
|
||||||
|
|
||||||
hass.bus.async_listen(const.EVENT_NODE_EVENT, listener)
|
|
||||||
|
|
||||||
# Test event before entity added to hass
|
|
||||||
value = 234
|
|
||||||
hass.async_add_job(mock_receivers[0], node, value)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert len(events) == 0
|
|
||||||
|
|
||||||
# Add entity to hass
|
|
||||||
entity.hass = hass
|
|
||||||
entity.entity_id = "zwave.mock_node"
|
|
||||||
|
|
||||||
value = 234
|
|
||||||
hass.async_add_job(mock_receivers[0], node, value)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(events) == 1
|
|
||||||
assert events[0].data[ATTR_ENTITY_ID] == "zwave.mock_node"
|
|
||||||
assert events[0].data[const.ATTR_NODE_ID] == 11
|
|
||||||
assert events[0].data[const.ATTR_BASIC_LEVEL] == value
|
|
||||||
|
|
||||||
|
|
||||||
async def test_scene_activated(hass, mock_openzwave):
|
|
||||||
"""Test scene activated event."""
|
|
||||||
mock_receivers = []
|
|
||||||
|
|
||||||
def mock_connect(receiver, signal, *args, **kwargs):
|
|
||||||
if signal == mock_zwave.MockNetwork.SIGNAL_SCENE_EVENT:
|
|
||||||
mock_receivers.append(receiver)
|
|
||||||
|
|
||||||
node = mock_zwave.MockNode(node_id=11)
|
|
||||||
|
|
||||||
with patch("pydispatch.dispatcher.connect", new=mock_connect):
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, mock_openzwave)
|
|
||||||
|
|
||||||
assert len(mock_receivers) == 1
|
|
||||||
|
|
||||||
events = []
|
|
||||||
|
|
||||||
def listener(event):
|
|
||||||
events.append(event)
|
|
||||||
|
|
||||||
hass.bus.async_listen(const.EVENT_SCENE_ACTIVATED, listener)
|
|
||||||
|
|
||||||
# Test event before entity added to hass
|
|
||||||
scene_id = 123
|
|
||||||
hass.async_add_job(mock_receivers[0], node, scene_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert len(events) == 0
|
|
||||||
|
|
||||||
# Add entity to hass
|
|
||||||
entity.hass = hass
|
|
||||||
entity.entity_id = "zwave.mock_node"
|
|
||||||
|
|
||||||
scene_id = 123
|
|
||||||
hass.async_add_job(mock_receivers[0], node, scene_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(events) == 1
|
|
||||||
assert events[0].data[ATTR_ENTITY_ID] == "zwave.mock_node"
|
|
||||||
assert events[0].data[const.ATTR_NODE_ID] == 11
|
|
||||||
assert events[0].data[const.ATTR_SCENE_ID] == scene_id
|
|
||||||
|
|
||||||
|
|
||||||
async def test_central_scene_activated(hass, mock_openzwave):
|
|
||||||
"""Test central scene activated event."""
|
|
||||||
mock_receivers = []
|
|
||||||
|
|
||||||
def mock_connect(receiver, signal, *args, **kwargs):
|
|
||||||
if signal == mock_zwave.MockNetwork.SIGNAL_VALUE_CHANGED:
|
|
||||||
mock_receivers.append(receiver)
|
|
||||||
|
|
||||||
node = mock_zwave.MockNode(node_id=11)
|
|
||||||
|
|
||||||
with patch("pydispatch.dispatcher.connect", new=mock_connect):
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, mock_openzwave)
|
|
||||||
|
|
||||||
assert len(mock_receivers) == 1
|
|
||||||
|
|
||||||
events = []
|
|
||||||
|
|
||||||
def listener(event):
|
|
||||||
events.append(event)
|
|
||||||
|
|
||||||
hass.bus.async_listen(const.EVENT_SCENE_ACTIVATED, listener)
|
|
||||||
|
|
||||||
# Test event before entity added to hass
|
|
||||||
scene_id = 1
|
|
||||||
scene_data = 3
|
|
||||||
value = mock_zwave.MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_CENTRAL_SCENE, index=scene_id, data=scene_data
|
|
||||||
)
|
|
||||||
hass.async_add_job(mock_receivers[0], node, value)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert len(events) == 0
|
|
||||||
|
|
||||||
# Add entity to hass
|
|
||||||
entity.hass = hass
|
|
||||||
entity.entity_id = "zwave.mock_node"
|
|
||||||
|
|
||||||
scene_id = 1
|
|
||||||
scene_data = 3
|
|
||||||
value = mock_zwave.MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_CENTRAL_SCENE, index=scene_id, data=scene_data
|
|
||||||
)
|
|
||||||
hass.async_add_job(mock_receivers[0], node, value)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(events) == 1
|
|
||||||
assert events[0].data[ATTR_ENTITY_ID] == "zwave.mock_node"
|
|
||||||
assert events[0].data[const.ATTR_NODE_ID] == 11
|
|
||||||
assert events[0].data[const.ATTR_SCENE_ID] == scene_id
|
|
||||||
assert events[0].data[const.ATTR_SCENE_DATA] == scene_data
|
|
||||||
|
|
||||||
|
|
||||||
async def test_application_version(hass, mock_openzwave):
|
|
||||||
"""Test application version."""
|
|
||||||
mock_receivers = {}
|
|
||||||
|
|
||||||
signal_mocks = [
|
|
||||||
mock_zwave.MockNetwork.SIGNAL_VALUE_CHANGED,
|
|
||||||
mock_zwave.MockNetwork.SIGNAL_VALUE_ADDED,
|
|
||||||
]
|
|
||||||
|
|
||||||
def mock_connect(receiver, signal, *args, **kwargs):
|
|
||||||
if signal in signal_mocks:
|
|
||||||
mock_receivers[signal] = receiver
|
|
||||||
|
|
||||||
node = mock_zwave.MockNode(node_id=11)
|
|
||||||
|
|
||||||
with patch("pydispatch.dispatcher.connect", new=mock_connect):
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, mock_openzwave)
|
|
||||||
|
|
||||||
for signal_mock in signal_mocks:
|
|
||||||
assert signal_mock in mock_receivers.keys()
|
|
||||||
|
|
||||||
events = []
|
|
||||||
|
|
||||||
def listener(event):
|
|
||||||
events.append(event)
|
|
||||||
|
|
||||||
# Make sure application version isn't set before
|
|
||||||
assert (
|
|
||||||
node_entity.ATTR_APPLICATION_VERSION not in entity.extra_state_attributes.keys()
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add entity to hass
|
|
||||||
entity.hass = hass
|
|
||||||
entity.entity_id = "zwave.mock_node"
|
|
||||||
|
|
||||||
# Fire off an added value
|
|
||||||
value = mock_zwave.MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_VERSION,
|
|
||||||
label="Application Version",
|
|
||||||
data="5.10",
|
|
||||||
)
|
|
||||||
hass.async_add_job(
|
|
||||||
mock_receivers[mock_zwave.MockNetwork.SIGNAL_VALUE_ADDED], node, value
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert entity.extra_state_attributes[node_entity.ATTR_APPLICATION_VERSION] == "5.10"
|
|
||||||
|
|
||||||
# Fire off a changed
|
|
||||||
value = mock_zwave.MockValue(
|
|
||||||
command_class=const.COMMAND_CLASS_VERSION,
|
|
||||||
label="Application Version",
|
|
||||||
data="4.14",
|
|
||||||
)
|
|
||||||
hass.async_add_job(
|
|
||||||
mock_receivers[mock_zwave.MockNetwork.SIGNAL_VALUE_CHANGED], node, value
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert entity.extra_state_attributes[node_entity.ATTR_APPLICATION_VERSION] == "4.14"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_network_node_changed_from_value(hass, mock_openzwave):
|
|
||||||
"""Test for network_node_changed."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
value = mock_zwave.MockValue(node=node)
|
|
||||||
with patch.object(entity, "maybe_schedule_update") as mock:
|
|
||||||
mock_zwave.value_changed(value)
|
|
||||||
mock.assert_called_once_with()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_network_node_changed_from_node(hass, mock_openzwave):
|
|
||||||
"""Test for network_node_changed."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
with patch.object(entity, "maybe_schedule_update") as mock:
|
|
||||||
mock_zwave.node_changed(node)
|
|
||||||
mock.assert_called_once_with()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_network_node_changed_from_another_node(hass, mock_openzwave):
|
|
||||||
"""Test for network_node_changed."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
with patch.object(entity, "maybe_schedule_update") as mock:
|
|
||||||
another_node = mock_zwave.MockNode(node_id=1024)
|
|
||||||
mock_zwave.node_changed(another_node)
|
|
||||||
assert not mock.called
|
|
||||||
|
|
||||||
|
|
||||||
async def test_network_node_changed_from_notification(hass, mock_openzwave):
|
|
||||||
"""Test for network_node_changed."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
with patch.object(entity, "maybe_schedule_update") as mock:
|
|
||||||
mock_zwave.notification(node_id=node.node_id)
|
|
||||||
mock.assert_called_once_with()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_network_node_changed_from_another_notification(hass, mock_openzwave):
|
|
||||||
"""Test for network_node_changed."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
with patch.object(entity, "maybe_schedule_update") as mock:
|
|
||||||
mock_zwave.notification(node_id=1024)
|
|
||||||
assert not mock.called
|
|
||||||
|
|
||||||
|
|
||||||
async def test_node_changed(hass, mock_openzwave):
|
|
||||||
"""Test node_changed function."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode(
|
|
||||||
query_stage="Dynamic",
|
|
||||||
is_awake=True,
|
|
||||||
is_ready=False,
|
|
||||||
is_failed=False,
|
|
||||||
is_info_received=True,
|
|
||||||
max_baud_rate=40000,
|
|
||||||
is_zwave_plus=False,
|
|
||||||
capabilities=[],
|
|
||||||
neighbors=[],
|
|
||||||
location=None,
|
|
||||||
)
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
|
|
||||||
assert {
|
|
||||||
"node_id": node.node_id,
|
|
||||||
"node_name": "Mock Node",
|
|
||||||
"manufacturer_name": "Test Manufacturer",
|
|
||||||
"product_name": "Test Product",
|
|
||||||
} == entity.extra_state_attributes
|
|
||||||
|
|
||||||
node.get_values.return_value = {1: mock_zwave.MockValue(data=1800)}
|
|
||||||
zwave_network.manager.getNodeStatistics.return_value = {
|
|
||||||
"receivedCnt": 4,
|
|
||||||
"ccData": [
|
|
||||||
{"receivedCnt": 0, "commandClassId": 134, "sentCnt": 0},
|
|
||||||
{"receivedCnt": 1, "commandClassId": 133, "sentCnt": 1},
|
|
||||||
{"receivedCnt": 1, "commandClassId": 115, "sentCnt": 1},
|
|
||||||
{"receivedCnt": 0, "commandClassId": 114, "sentCnt": 0},
|
|
||||||
{"receivedCnt": 0, "commandClassId": 112, "sentCnt": 0},
|
|
||||||
{"receivedCnt": 1, "commandClassId": 32, "sentCnt": 1},
|
|
||||||
{"receivedCnt": 0, "commandClassId": 0, "sentCnt": 0},
|
|
||||||
],
|
|
||||||
"receivedUnsolicited": 0,
|
|
||||||
"sentTS": "2017-03-27 15:38:15:620 ",
|
|
||||||
"averageRequestRTT": 2462,
|
|
||||||
"lastResponseRTT": 3679,
|
|
||||||
"retries": 0,
|
|
||||||
"sentFailed": 1,
|
|
||||||
"sentCnt": 7,
|
|
||||||
"quality": 0,
|
|
||||||
"lastRequestRTT": 1591,
|
|
||||||
"lastReceivedMessage": [
|
|
||||||
0,
|
|
||||||
4,
|
|
||||||
0,
|
|
||||||
15,
|
|
||||||
3,
|
|
||||||
32,
|
|
||||||
3,
|
|
||||||
0,
|
|
||||||
221,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
"receivedDups": 1,
|
|
||||||
"averageResponseRTT": 2443,
|
|
||||||
"receivedTS": "2017-03-27 15:38:19:298 ",
|
|
||||||
}
|
|
||||||
entity.node_changed()
|
|
||||||
assert {
|
|
||||||
"node_id": node.node_id,
|
|
||||||
"node_name": "Mock Node",
|
|
||||||
"manufacturer_name": "Test Manufacturer",
|
|
||||||
"product_name": "Test Product",
|
|
||||||
"query_stage": "Dynamic",
|
|
||||||
"is_awake": True,
|
|
||||||
"is_ready": False,
|
|
||||||
"is_failed": False,
|
|
||||||
"is_info_received": True,
|
|
||||||
"max_baud_rate": 40000,
|
|
||||||
"is_zwave_plus": False,
|
|
||||||
"battery_level": 42,
|
|
||||||
"wake_up_interval": 1800,
|
|
||||||
"averageRequestRTT": 2462,
|
|
||||||
"averageResponseRTT": 2443,
|
|
||||||
"lastRequestRTT": 1591,
|
|
||||||
"lastResponseRTT": 3679,
|
|
||||||
"receivedCnt": 4,
|
|
||||||
"receivedDups": 1,
|
|
||||||
"receivedTS": "2017-03-27 15:38:19:298 ",
|
|
||||||
"receivedUnsolicited": 0,
|
|
||||||
"retries": 0,
|
|
||||||
"sentCnt": 7,
|
|
||||||
"sentFailed": 1,
|
|
||||||
"sentTS": "2017-03-27 15:38:15:620 ",
|
|
||||||
} == entity.extra_state_attributes
|
|
||||||
|
|
||||||
node.can_wake_up_value = False
|
|
||||||
entity.node_changed()
|
|
||||||
|
|
||||||
assert "wake_up_interval" not in entity.extra_state_attributes
|
|
||||||
|
|
||||||
|
|
||||||
async def test_name(hass, mock_openzwave):
|
|
||||||
"""Test name property."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
assert entity.name == "Mock Node"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_state_before_update(hass, mock_openzwave):
|
|
||||||
"""Test state before update was called."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
assert entity.state is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_state_not_ready(hass, mock_openzwave):
|
|
||||||
"""Test state property."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode(
|
|
||||||
query_stage="Dynamic",
|
|
||||||
is_awake=True,
|
|
||||||
is_ready=False,
|
|
||||||
is_failed=False,
|
|
||||||
is_info_received=True,
|
|
||||||
)
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
|
|
||||||
node.is_ready = False
|
|
||||||
entity.node_changed()
|
|
||||||
assert entity.state == "initializing"
|
|
||||||
|
|
||||||
node.is_failed = True
|
|
||||||
node.query_stage = "Complete"
|
|
||||||
entity.node_changed()
|
|
||||||
assert entity.state == "dead"
|
|
||||||
|
|
||||||
node.is_failed = False
|
|
||||||
node.is_awake = False
|
|
||||||
entity.node_changed()
|
|
||||||
assert entity.state == "sleeping"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_state_ready(hass, mock_openzwave):
|
|
||||||
"""Test state property."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode(
|
|
||||||
query_stage="Dynamic",
|
|
||||||
is_awake=True,
|
|
||||||
is_ready=False,
|
|
||||||
is_failed=False,
|
|
||||||
is_info_received=True,
|
|
||||||
)
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
|
|
||||||
node.query_stage = "Complete"
|
|
||||||
node.is_ready = True
|
|
||||||
entity.node_changed()
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert entity.state == "ready"
|
|
||||||
|
|
||||||
node.is_failed = True
|
|
||||||
entity.node_changed()
|
|
||||||
assert entity.state == "dead"
|
|
||||||
|
|
||||||
node.is_failed = False
|
|
||||||
node.is_awake = False
|
|
||||||
entity.node_changed()
|
|
||||||
assert entity.state == "sleeping"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_not_polled(hass, mock_openzwave):
|
|
||||||
"""Test should_poll property."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
assert not entity.should_poll
|
|
||||||
|
|
||||||
|
|
||||||
async def test_unique_id(hass, mock_openzwave):
|
|
||||||
"""Test unique_id."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
assert entity.unique_id == "node-567"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_unique_id_missing_data(hass, mock_openzwave):
|
|
||||||
"""Test unique_id."""
|
|
||||||
zwave_network = MagicMock()
|
|
||||||
node = mock_zwave.MockNode()
|
|
||||||
node.manufacturer_name = None
|
|
||||||
node.name = None
|
|
||||||
node.is_ready = False
|
|
||||||
entity = node_entity.ZWaveNodeEntity(node, zwave_network)
|
|
||||||
|
|
||||||
assert entity.unique_id is None
|
|
|
@ -1,183 +0,0 @@
|
||||||
"""Test Z-Wave sensor."""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorDeviceClass
|
|
||||||
from homeassistant.components.zwave import const, sensor
|
|
||||||
import homeassistant.const
|
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
|
||||||
|
|
||||||
# Integration is disabled
|
|
||||||
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_none(mock_openzwave):
|
|
||||||
"""Test get_device returns None."""
|
|
||||||
node = MockNode()
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert device is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_alarmsensor(mock_openzwave):
|
|
||||||
"""Test get_device returns a Z-Wave alarmsensor."""
|
|
||||||
node = MockNode(
|
|
||||||
command_classes=[const.COMMAND_CLASS_ALARM, const.COMMAND_CLASS_SENSOR_ALARM]
|
|
||||||
)
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, sensor.ZWaveAlarmSensor)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_multilevelsensor(mock_openzwave):
|
|
||||||
"""Test get_device returns a Z-Wave multilevel sensor."""
|
|
||||||
node = MockNode(
|
|
||||||
command_classes=[
|
|
||||||
const.COMMAND_CLASS_SENSOR_MULTILEVEL,
|
|
||||||
const.COMMAND_CLASS_METER,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
value = MockValue(data=0, node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, sensor.ZWaveMultilevelSensor)
|
|
||||||
assert device.force_update
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_multilevel_meter(mock_openzwave):
|
|
||||||
"""Test get_device returns a Z-Wave multilevel sensor."""
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_METER])
|
|
||||||
value = MockValue(data=0, node=node, type=const.TYPE_DECIMAL)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, sensor.ZWaveMultilevelSensor)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_battery_sensor(mock_openzwave):
|
|
||||||
"""Test get_device returns a Z-Wave battery sensor."""
|
|
||||||
|
|
||||||
node = MockNode(command_classes=[const.COMMAND_CLASS_BATTERY])
|
|
||||||
value = MockValue(
|
|
||||||
data=0,
|
|
||||||
node=node,
|
|
||||||
type=const.TYPE_DECIMAL,
|
|
||||||
command_class=const.COMMAND_CLASS_BATTERY,
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
assert isinstance(device, sensor.ZWaveBatterySensor)
|
|
||||||
assert device.device_class is SensorDeviceClass.BATTERY
|
|
||||||
|
|
||||||
|
|
||||||
def test_multilevelsensor_value_changed_temp_fahrenheit(hass, mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave multilevel sensor for temperature."""
|
|
||||||
hass.config.units.temperature_unit = homeassistant.const.TEMP_FAHRENHEIT
|
|
||||||
|
|
||||||
node = MockNode(
|
|
||||||
command_classes=[
|
|
||||||
const.COMMAND_CLASS_SENSOR_MULTILEVEL,
|
|
||||||
const.COMMAND_CLASS_METER,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
value = MockValue(data=190.95555, units="F", node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
device.hass = hass
|
|
||||||
assert device.state == 191.0
|
|
||||||
assert device.unit_of_measurement == homeassistant.const.TEMP_FAHRENHEIT
|
|
||||||
assert device.device_class is SensorDeviceClass.TEMPERATURE
|
|
||||||
value.data = 197.95555
|
|
||||||
value_changed(value)
|
|
||||||
assert device.state == 198.0
|
|
||||||
|
|
||||||
|
|
||||||
def test_multilevelsensor_value_changed_temp_celsius(hass, mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave multilevel sensor for temperature."""
|
|
||||||
hass.config.units.temperature_unit = homeassistant.const.TEMP_CELSIUS
|
|
||||||
node = MockNode(
|
|
||||||
command_classes=[
|
|
||||||
const.COMMAND_CLASS_SENSOR_MULTILEVEL,
|
|
||||||
const.COMMAND_CLASS_METER,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
value = MockValue(data=38.85555, units="C", node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
device.hass = hass
|
|
||||||
assert device.state == 38.9
|
|
||||||
assert device.unit_of_measurement == homeassistant.const.TEMP_CELSIUS
|
|
||||||
assert device.device_class is SensorDeviceClass.TEMPERATURE
|
|
||||||
value.data = 37.95555
|
|
||||||
value_changed(value)
|
|
||||||
assert device.state == 38.0
|
|
||||||
|
|
||||||
|
|
||||||
def test_multilevelsensor_value_changed_other_units(hass, mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave multilevel sensor for other units."""
|
|
||||||
node = MockNode(
|
|
||||||
command_classes=[
|
|
||||||
const.COMMAND_CLASS_SENSOR_MULTILEVEL,
|
|
||||||
const.COMMAND_CLASS_METER,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
value = MockValue(
|
|
||||||
data=190.95555, units=homeassistant.const.ENERGY_KILO_WATT_HOUR, node=node
|
|
||||||
)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
device.hass = hass
|
|
||||||
assert device.state == 190.96
|
|
||||||
assert device.unit_of_measurement == homeassistant.const.ENERGY_KILO_WATT_HOUR
|
|
||||||
assert device.device_class is None
|
|
||||||
value.data = 197.95555
|
|
||||||
value_changed(value)
|
|
||||||
assert device.state == 197.96
|
|
||||||
|
|
||||||
|
|
||||||
def test_multilevelsensor_value_changed_integer(hass, mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave multilevel sensor for other units."""
|
|
||||||
node = MockNode(
|
|
||||||
command_classes=[
|
|
||||||
const.COMMAND_CLASS_SENSOR_MULTILEVEL,
|
|
||||||
const.COMMAND_CLASS_METER,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
value = MockValue(data=5, units="counts", node=node)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
device.hass = hass
|
|
||||||
assert device.state == 5
|
|
||||||
assert device.unit_of_measurement == "counts"
|
|
||||||
assert device.device_class is None
|
|
||||||
value.data = 6
|
|
||||||
value_changed(value)
|
|
||||||
assert device.state == 6
|
|
||||||
|
|
||||||
|
|
||||||
def test_alarm_sensor_value_changed(hass, mock_openzwave):
|
|
||||||
"""Test value changed for Z-Wave sensor."""
|
|
||||||
node = MockNode(
|
|
||||||
command_classes=[const.COMMAND_CLASS_ALARM, const.COMMAND_CLASS_SENSOR_ALARM]
|
|
||||||
)
|
|
||||||
value = MockValue(data=12.34, node=node, units=homeassistant.const.PERCENTAGE)
|
|
||||||
values = MockEntityValues(primary=value)
|
|
||||||
|
|
||||||
device = sensor.get_device(node=node, values=values, node_config={})
|
|
||||||
device.hass = hass
|
|
||||||
assert device.state == 12.34
|
|
||||||
assert device.unit_of_measurement == homeassistant.const.PERCENTAGE
|
|
||||||
assert device.device_class is None
|
|
||||||
value.data = 45.67
|
|
||||||
value_changed(value)
|
|
||||||
assert device.state == 45.67
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue