Merge remote-tracking branch 'upstream/master' into climate
This commit is contained in:
20
README.md
20
README.md
@@ -44,6 +44,8 @@ localtuya:
|
|||||||
local_key: xxxxx
|
local_key: xxxxx
|
||||||
friendly_name: Tuya Device
|
friendly_name: Tuya Device
|
||||||
protocol_version: "3.3"
|
protocol_version: "3.3"
|
||||||
|
scan_interval: # optional, only needed if energy monitoring values are not updating
|
||||||
|
seconds: 30 # Values less than 10 seconds may cause stability issues
|
||||||
entities:
|
entities:
|
||||||
- platform: binary_sensor
|
- platform: binary_sensor
|
||||||
friendly_name: Plug Status
|
friendly_name: Plug Status
|
||||||
@@ -63,7 +65,16 @@ localtuya:
|
|||||||
|
|
||||||
- platform: fan
|
- platform: fan
|
||||||
friendly_name: Device Fan
|
friendly_name: Device Fan
|
||||||
id: 3
|
id: 3 # dps for on/off state
|
||||||
|
fan_direction: 4 # Optional, dps for fan direction
|
||||||
|
fan_direction_fwd: forward # String for the forward direction
|
||||||
|
fan_direction_rev: reverse # String for the reverse direction
|
||||||
|
fan_ordered_list: low,medium,high,auto # Optional, If this is used it will not use the min and max integers.
|
||||||
|
fan_oscilating_control: 4 # Optional, dps for fan osciallation
|
||||||
|
fan_speed_control: 3 # Optional, if ordered list not used, dps for speed control
|
||||||
|
fan_speed_min: 1 # Optional, if ordered list not used, minimum integer for speed range
|
||||||
|
fan_speed_max: 10 # Optional, if ordered list not used, maximum integer for speed range
|
||||||
|
|
||||||
|
|
||||||
- platform: light
|
- platform: light
|
||||||
friendly_name: Device Light
|
friendly_name: Device Light
|
||||||
@@ -112,9 +123,13 @@ select one of these, or manually input all the parameters.
|
|||||||

|

|
||||||
|
|
||||||
If you have selected one entry, you only need to input the device's Friendly Name and the localKey.
|
If you have selected one entry, you only need to input the device's Friendly Name and the localKey.
|
||||||
|
|
||||||
|
Setting the scan interval is optional, only needed if energy/power values are not updating frequently enough by default. Values less than 10 seconds may cause stability issues.
|
||||||
|
|
||||||
Once you press "Submit", the connection is tested to check that everything works.
|
Once you press "Submit", the connection is tested to check that everything works.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
Then, it's time to add the entities: this step will take place several times. First, select the entity type from the drop-down menu to set it up.
|
Then, it's time to add the entities: this step will take place several times. First, select the entity type from the drop-down menu to set it up.
|
||||||
After you have defined all the needed entities, leave the "Do not add more entities" checkbox checked: this will complete the procedure.
|
After you have defined all the needed entities, leave the "Do not add more entities" checkbox checked: this will complete the procedure.
|
||||||
@@ -140,6 +155,7 @@ You can obtain Energy monitoring (voltage, current) in two different ways:
|
|||||||
Note: Voltage and Consumption usually include the first decimal. You will need to scale the parament by 0.1 to get the correct values.
|
Note: Voltage and Consumption usually include the first decimal. You will need to scale the parament by 0.1 to get the correct values.
|
||||||
1) Access the voltage/current/current_consumption attributes of a switch, and define template sensors
|
1) Access the voltage/current/current_consumption attributes of a switch, and define template sensors
|
||||||
Note: these values are already divided by 10 for Voltage and Consumption
|
Note: these values are already divided by 10 for Voltage and Consumption
|
||||||
|
1) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended.
|
||||||
|
|
||||||
```
|
```
|
||||||
sensor:
|
sensor:
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
"""Code shared between all platforms."""
|
"""Code shared between all platforms."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
@@ -9,8 +10,10 @@ from homeassistant.const import (
|
|||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
|
CONF_SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
@@ -117,6 +120,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self._is_closing = False
|
self._is_closing = False
|
||||||
self._connect_task = None
|
self._connect_task = None
|
||||||
self._disconnect_task = None
|
self._disconnect_task = None
|
||||||
|
self._unsub_interval = None
|
||||||
self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID])
|
self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID])
|
||||||
|
|
||||||
# This has to be done in case the device type is type_0d
|
# This has to be done in case the device type is type_0d
|
||||||
@@ -166,6 +170,16 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self._disconnect_task = async_dispatcher_connect(
|
self._disconnect_task = async_dispatcher_connect(
|
||||||
self._hass, signal, _new_entity_handler
|
self._hass, signal, _new_entity_handler
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
CONF_SCAN_INTERVAL in self._config_entry
|
||||||
|
and self._config_entry[CONF_SCAN_INTERVAL] > 0
|
||||||
|
):
|
||||||
|
self._unsub_interval = async_track_time_interval(
|
||||||
|
self._hass,
|
||||||
|
self._async_refresh,
|
||||||
|
timedelta(seconds=self._config_entry[CONF_SCAN_INTERVAL]),
|
||||||
|
)
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed")
|
self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed")
|
||||||
if self._interface is not None:
|
if self._interface is not None:
|
||||||
@@ -173,6 +187,10 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self._interface = None
|
self._interface = None
|
||||||
self._connect_task = None
|
self._connect_task = None
|
||||||
|
|
||||||
|
async def _async_refresh(self, _now):
|
||||||
|
if self._interface is not None:
|
||||||
|
await self._interface.update_dps()
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
"""Close connection and stop re-connect loop."""
|
"""Close connection and stop re-connect loop."""
|
||||||
self._is_closing = True
|
self._is_closing = True
|
||||||
@@ -223,7 +241,9 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
"""Device disconnected."""
|
"""Device disconnected."""
|
||||||
signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}"
|
||||||
async_dispatcher_send(self._hass, signal, None)
|
async_dispatcher_send(self._hass, signal, None)
|
||||||
|
if self._unsub_interval is not None:
|
||||||
|
self._unsub_interval()
|
||||||
|
self._unsub_interval = None
|
||||||
self._interface = None
|
self._interface = None
|
||||||
self.debug("Disconnected - waiting for discovery broadcast")
|
self.debug("Disconnected - waiting for discovery broadcast")
|
||||||
|
|
||||||
@@ -253,13 +273,13 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
|
|
||||||
def _update_handler(status):
|
def _update_handler(status):
|
||||||
"""Update entity state when status was updated."""
|
"""Update entity state when status was updated."""
|
||||||
if status is not None:
|
if status is None:
|
||||||
self._status = status
|
status = {}
|
||||||
self.status_updated()
|
if self._status != status:
|
||||||
else:
|
self._status = status.copy()
|
||||||
self._status = {}
|
if status:
|
||||||
|
self.status_updated()
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}"
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ from homeassistant.const import (
|
|||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
|
CONF_SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ BASIC_INFO_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_HOST): str,
|
vol.Required(CONF_HOST): str,
|
||||||
vol.Required(CONF_DEVICE_ID): str,
|
vol.Required(CONF_DEVICE_ID): str,
|
||||||
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
||||||
|
vol.Optional(CONF_SCAN_INTERVAL): int,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,6 +57,7 @@ DEVICE_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_LOCAL_KEY): cv.string,
|
vol.Required(CONF_LOCAL_KEY): cv.string,
|
||||||
vol.Required(CONF_FRIENDLY_NAME): cv.string,
|
vol.Required(CONF_FRIENDLY_NAME): cv.string,
|
||||||
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
||||||
|
vol.Optional(CONF_SCAN_INTERVAL): int,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,6 +93,7 @@ def options_schema(entities):
|
|||||||
vol.Required(CONF_HOST): str,
|
vol.Required(CONF_HOST): str,
|
||||||
vol.Required(CONF_LOCAL_KEY): str,
|
vol.Required(CONF_LOCAL_KEY): str,
|
||||||
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
||||||
|
vol.Optional(CONF_SCAN_INTERVAL): int,
|
||||||
vol.Required(
|
vol.Required(
|
||||||
CONF_ENTITIES, description={"suggested_value": entity_names}
|
CONF_ENTITIES, description={"suggested_value": entity_names}
|
||||||
): cv.multi_select(entity_names),
|
): cv.multi_select(entity_names),
|
||||||
|
@@ -35,9 +35,12 @@ CONF_SPAN_TIME = "span_time"
|
|||||||
# fan
|
# fan
|
||||||
CONF_FAN_SPEED_CONTROL = "fan_speed_control"
|
CONF_FAN_SPEED_CONTROL = "fan_speed_control"
|
||||||
CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control"
|
CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control"
|
||||||
CONF_FAN_SPEED_LOW = "fan_speed_low"
|
CONF_FAN_SPEED_MIN = "fan_speed_min"
|
||||||
CONF_FAN_SPEED_MEDIUM = "fan_speed_medium"
|
CONF_FAN_SPEED_MAX = "fan_speed_max"
|
||||||
CONF_FAN_SPEED_HIGH = "fan_speed_high"
|
CONF_FAN_ORDERED_LIST = "fan_speed_ordered_list"
|
||||||
|
CONF_FAN_DIRECTION = "fan_direction"
|
||||||
|
CONF_FAN_DIRECTION_FWD = "fan_direction_forward"
|
||||||
|
CONF_FAN_DIRECTION_REV = "fan_direction_reverse"
|
||||||
|
|
||||||
# sensor
|
# sensor
|
||||||
CONF_SCALING = "scaling"
|
CONF_SCALING = "scaling"
|
||||||
|
@@ -1,26 +1,37 @@
|
|||||||
"""Platform to locally control Tuya-based fan devices."""
|
"""Platform to locally control Tuya-based fan devices."""
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant.components.fan import (
|
from homeassistant.components.fan import (
|
||||||
|
DIRECTION_FORWARD,
|
||||||
|
DIRECTION_REVERSE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SPEED_HIGH,
|
SUPPORT_DIRECTION,
|
||||||
SPEED_LOW,
|
|
||||||
SPEED_MEDIUM,
|
|
||||||
SPEED_OFF,
|
|
||||||
SUPPORT_OSCILLATE,
|
SUPPORT_OSCILLATE,
|
||||||
SUPPORT_SET_SPEED,
|
SUPPORT_SET_SPEED,
|
||||||
FanEntity,
|
FanEntity,
|
||||||
)
|
)
|
||||||
|
from homeassistant.util.percentage import (
|
||||||
|
int_states_in_range,
|
||||||
|
ordered_list_item_to_percentage,
|
||||||
|
percentage_to_ordered_list_item,
|
||||||
|
percentage_to_ranged_value,
|
||||||
|
ranged_value_to_percentage,
|
||||||
|
)
|
||||||
|
|
||||||
from .common import LocalTuyaEntity, async_setup_entry
|
from .common import LocalTuyaEntity, async_setup_entry
|
||||||
from .const import (
|
from .const import (
|
||||||
|
CONF_FAN_DIRECTION,
|
||||||
|
CONF_FAN_DIRECTION_FWD,
|
||||||
|
CONF_FAN_DIRECTION_REV,
|
||||||
|
CONF_FAN_ORDERED_LIST,
|
||||||
CONF_FAN_OSCILLATING_CONTROL,
|
CONF_FAN_OSCILLATING_CONTROL,
|
||||||
CONF_FAN_SPEED_CONTROL,
|
CONF_FAN_SPEED_CONTROL,
|
||||||
CONF_FAN_SPEED_HIGH,
|
CONF_FAN_SPEED_MAX,
|
||||||
CONF_FAN_SPEED_LOW,
|
CONF_FAN_SPEED_MIN,
|
||||||
CONF_FAN_SPEED_MEDIUM,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -31,15 +42,12 @@ def flow_schema(dps):
|
|||||||
return {
|
return {
|
||||||
vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps),
|
vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps),
|
||||||
vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps),
|
vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps),
|
||||||
vol.Optional(CONF_FAN_SPEED_LOW, default=SPEED_LOW): vol.In(
|
vol.Optional(CONF_FAN_DIRECTION): vol.In(dps),
|
||||||
[SPEED_LOW, "1", "2", "small"]
|
vol.Optional(CONF_FAN_DIRECTION_FWD, default="forward"): cv.string,
|
||||||
),
|
vol.Optional(CONF_FAN_DIRECTION_REV, default="reverse"): cv.string,
|
||||||
vol.Optional(CONF_FAN_SPEED_MEDIUM, default=SPEED_MEDIUM): vol.In(
|
vol.Optional(CONF_FAN_SPEED_MIN, default=1): cv.positive_int,
|
||||||
[SPEED_MEDIUM, "mid", "2", "3"]
|
vol.Optional(CONF_FAN_SPEED_MAX, default=9): cv.positive_int,
|
||||||
),
|
vol.Optional(CONF_FAN_ORDERED_LIST, default="disabled"): cv.string,
|
||||||
vol.Optional(CONF_FAN_SPEED_HIGH, default=SPEED_HIGH): vol.In(
|
|
||||||
[SPEED_HIGH, "auto", "3", "4", "large", "big"]
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -56,28 +64,46 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity):
|
|||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
super().__init__(device, config_entry, fanid, _LOGGER, **kwargs)
|
super().__init__(device, config_entry, fanid, _LOGGER, **kwargs)
|
||||||
self._is_on = False
|
self._is_on = False
|
||||||
self._speed = None
|
|
||||||
self._oscillating = None
|
self._oscillating = None
|
||||||
|
self._direction = None
|
||||||
|
self._percentage = None
|
||||||
|
self._speed_range = (
|
||||||
|
self._config.get(CONF_FAN_SPEED_MIN),
|
||||||
|
self._config.get(CONF_FAN_SPEED_MAX),
|
||||||
|
)
|
||||||
|
self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",")
|
||||||
|
self._ordered_list_mode = None
|
||||||
|
|
||||||
|
if isinstance(self._ordered_list, list) and len(self._ordered_list) > 1:
|
||||||
|
self._use_ordered_list = True
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Fan _use_ordered_list: %s > %s",
|
||||||
|
self._use_ordered_list,
|
||||||
|
self._ordered_list,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._use_ordered_list = False
|
||||||
|
_LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def oscillating(self):
|
def oscillating(self):
|
||||||
"""Return current oscillating status."""
|
"""Return current oscillating status."""
|
||||||
return self._oscillating
|
return self._oscillating
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_direction(self):
|
||||||
|
"""Return the current direction of the fan."""
|
||||||
|
return self._direction
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Check if Tuya fan is on."""
|
"""Check if Tuya fan is on."""
|
||||||
return self._is_on
|
return self._is_on
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def speed(self) -> str:
|
def percentage(self):
|
||||||
"""Return the current speed."""
|
"""Return the current percentage."""
|
||||||
return self._speed
|
return self._percentage
|
||||||
|
|
||||||
@property
|
|
||||||
def speed_list(self) -> list:
|
|
||||||
"""Get the list of available speeds."""
|
|
||||||
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
|
||||||
|
|
||||||
async def async_turn_on(
|
async def async_turn_on(
|
||||||
self,
|
self,
|
||||||
@@ -87,76 +113,143 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity):
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Turn on the entity."""
|
"""Turn on the entity."""
|
||||||
|
_LOGGER.debug("Fan async_turn_on")
|
||||||
await self._device.set_dp(True, self._dp_id)
|
await self._device.set_dp(True, self._dp_id)
|
||||||
if speed is not None:
|
if percentage is not None:
|
||||||
await self.async_set_speed(speed)
|
await self.async_set_percentage(percentage)
|
||||||
else:
|
else:
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs) -> None:
|
async def async_turn_off(self, **kwargs) -> None:
|
||||||
"""Turn off the entity."""
|
"""Turn off the entity."""
|
||||||
|
_LOGGER.debug("Fan async_turn_off")
|
||||||
|
|
||||||
await self._device.set_dp(False, self._dp_id)
|
await self._device.set_dp(False, self._dp_id)
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
async def async_set_speed(self, speed: str) -> None:
|
async def async_set_percentage(self, percentage):
|
||||||
"""Set the speed of the fan."""
|
"""Set the speed of the fan."""
|
||||||
mapping = {
|
_LOGGER.debug("Fan async_set_percentage: %s", percentage)
|
||||||
SPEED_LOW: self._config.get(CONF_FAN_SPEED_LOW),
|
|
||||||
SPEED_MEDIUM: self._config.get(CONF_FAN_SPEED_MEDIUM),
|
|
||||||
SPEED_HIGH: self._config.get(CONF_FAN_SPEED_HIGH),
|
|
||||||
}
|
|
||||||
|
|
||||||
if speed == SPEED_OFF:
|
if percentage is not None:
|
||||||
await self._device.set_dp(False, self._dp_id)
|
if percentage == 0:
|
||||||
else:
|
return await self.async_turn_off()
|
||||||
await self._device.set_dp(
|
if not self.is_on:
|
||||||
mapping.get(speed), self._config.get(CONF_FAN_SPEED_CONTROL)
|
await self.async_turn_on()
|
||||||
)
|
if self._use_ordered_list:
|
||||||
|
await self._device.set_dp(
|
||||||
|
str(
|
||||||
|
percentage_to_ordered_list_item(self._ordered_list, percentage)
|
||||||
|
),
|
||||||
|
self._config.get(CONF_FAN_SPEED_CONTROL),
|
||||||
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Fan async_set_percentage: %s > %s",
|
||||||
|
percentage,
|
||||||
|
percentage_to_ordered_list_item(self._ordered_list, percentage),
|
||||||
|
)
|
||||||
|
|
||||||
self.schedule_update_ha_state()
|
else:
|
||||||
|
await self._device.set_dp(
|
||||||
|
str(
|
||||||
|
math.ceil(
|
||||||
|
percentage_to_ranged_value(self._speed_range, percentage)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
self._config.get(CONF_FAN_SPEED_CONTROL),
|
||||||
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Fan async_set_percentage: %s > %s",
|
||||||
|
percentage,
|
||||||
|
percentage_to_ranged_value(self._speed_range, percentage),
|
||||||
|
)
|
||||||
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
async def async_oscillate(self, oscillating: bool) -> None:
|
async def async_oscillate(self, oscillating: bool) -> None:
|
||||||
"""Set oscillation."""
|
"""Set oscillation."""
|
||||||
|
_LOGGER.debug("Fan async_oscillate: %s", oscillating)
|
||||||
await self._device.set_dp(
|
await self._device.set_dp(
|
||||||
oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL)
|
oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL)
|
||||||
)
|
)
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
|
async def async_set_direction(self, direction):
|
||||||
|
"""Set the direction of the fan."""
|
||||||
|
_LOGGER.debug("Fan async_set_direction: %s", direction)
|
||||||
|
|
||||||
|
if direction == DIRECTION_FORWARD:
|
||||||
|
value = self._config.get(CONF_FAN_DIRECTION_FWD)
|
||||||
|
|
||||||
|
if direction == DIRECTION_REVERSE:
|
||||||
|
value = self._config.get(CONF_FAN_DIRECTION_REV)
|
||||||
|
await self._device.set_dp(value, self._config.get(CONF_FAN_DIRECTION))
|
||||||
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
supports = 0
|
features = 0
|
||||||
|
|
||||||
if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
|
if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
|
||||||
supports |= SUPPORT_OSCILLATE
|
features |= SUPPORT_OSCILLATE
|
||||||
if self.has_config(CONF_FAN_SPEED_CONTROL):
|
|
||||||
supports |= SUPPORT_SET_SPEED
|
|
||||||
|
|
||||||
return supports
|
if self.has_config(CONF_FAN_SPEED_CONTROL):
|
||||||
|
features |= SUPPORT_SET_SPEED
|
||||||
|
|
||||||
|
if self.has_config(CONF_FAN_DIRECTION):
|
||||||
|
features |= SUPPORT_DIRECTION
|
||||||
|
|
||||||
|
return features
|
||||||
|
|
||||||
|
@property
|
||||||
|
def speed_count(self) -> int:
|
||||||
|
"""Speed count for the fan."""
|
||||||
|
speed_count = int_states_in_range(self._speed_range)
|
||||||
|
_LOGGER.debug("Fan speed_count: %s", speed_count)
|
||||||
|
return speed_count
|
||||||
|
|
||||||
def status_updated(self):
|
def status_updated(self):
|
||||||
"""Get state of Tuya fan."""
|
"""Get state of Tuya fan."""
|
||||||
mappings = {
|
|
||||||
self._config.get(CONF_FAN_SPEED_LOW): SPEED_LOW,
|
|
||||||
self._config.get(CONF_FAN_SPEED_MEDIUM): SPEED_MEDIUM,
|
|
||||||
self._config.get(CONF_FAN_SPEED_HIGH): SPEED_HIGH,
|
|
||||||
}
|
|
||||||
|
|
||||||
self._is_on = self.dps(self._dp_id)
|
self._is_on = self.dps(self._dp_id)
|
||||||
|
|
||||||
if self.has_config(CONF_FAN_SPEED_CONTROL):
|
current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL)
|
||||||
self._speed = mappings.get(self.dps_conf(CONF_FAN_SPEED_CONTROL))
|
if self._use_ordered_list:
|
||||||
if self.speed is None:
|
_LOGGER.debug(
|
||||||
self.warning(
|
"Fan current_speed ordered_list_item_to_percentage: %s from %s",
|
||||||
"%s/%s: Ignoring unknown fan controller state: %s",
|
current_speed,
|
||||||
self.name,
|
self._ordered_list,
|
||||||
self.entity_id,
|
)
|
||||||
self.dps_conf(CONF_FAN_SPEED_CONTROL),
|
if current_speed is not None:
|
||||||
|
self._percentage = ordered_list_item_to_percentage(
|
||||||
|
self._ordered_list, current_speed
|
||||||
)
|
)
|
||||||
self._speed = None
|
|
||||||
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Fan current_speed ranged_value_to_percentage: %s from %s",
|
||||||
|
current_speed,
|
||||||
|
self._speed_range,
|
||||||
|
)
|
||||||
|
if current_speed is not None:
|
||||||
|
self._percentage = ranged_value_to_percentage(
|
||||||
|
self._speed_range, int(current_speed)
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER.debug("Fan current_percentage: %s", self._percentage)
|
||||||
|
|
||||||
if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
|
if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
|
||||||
self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL)
|
self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL)
|
||||||
|
_LOGGER.debug("Fan current_oscillating : %s", self._oscillating)
|
||||||
|
|
||||||
|
if self.has_config(CONF_FAN_DIRECTION):
|
||||||
|
value = self.dps_conf(CONF_FAN_DIRECTION)
|
||||||
|
if value is not None:
|
||||||
|
if value == self._config.get(CONF_FAN_DIRECTION_FWD):
|
||||||
|
self._direction = DIRECTION_FORWARD
|
||||||
|
|
||||||
|
if value == self._config.get(CONF_FAN_DIRECTION_REV):
|
||||||
|
self._direction = DIRECTION_REVERSE
|
||||||
|
_LOGGER.debug("Fan current_direction : %s > %s", value, self._direction)
|
||||||
|
|
||||||
|
|
||||||
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema)
|
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema)
|
||||||
|
@@ -4,10 +4,7 @@ from functools import partial
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant.components.number import DOMAIN, NumberEntity
|
from homeassistant.components.number import DOMAIN, NumberEntity
|
||||||
from homeassistant.const import (
|
from homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN
|
||||||
CONF_DEVICE_CLASS,
|
|
||||||
STATE_UNKNOWN,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .common import LocalTuyaEntity, async_setup_entry
|
from .common import LocalTuyaEntity, async_setup_entry
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@ Functions
|
|||||||
json = status() # returns json payload
|
json = status() # returns json payload
|
||||||
set_version(version) # 3.1 [default] or 3.3
|
set_version(version) # 3.1 [default] or 3.3
|
||||||
detect_available_dps() # returns a list of available dps provided by the device
|
detect_available_dps() # returns a list of available dps provided by the device
|
||||||
|
update_dps(dps) # sends update dps command
|
||||||
add_dps_to_request(dp_index) # adds dp_index to the list of dps used by the
|
add_dps_to_request(dp_index) # adds dp_index to the list of dps used by the
|
||||||
# device (to be queried in the payload)
|
# device (to be queried in the payload)
|
||||||
set_dp(on, dp_index) # Set value of any dps index.
|
set_dp(on, dp_index) # Set value of any dps index.
|
||||||
@@ -61,6 +62,7 @@ TuyaMessage = namedtuple("TuyaMessage", "seqno cmd retcode payload crc")
|
|||||||
SET = "set"
|
SET = "set"
|
||||||
STATUS = "status"
|
STATUS = "status"
|
||||||
HEARTBEAT = "heartbeat"
|
HEARTBEAT = "heartbeat"
|
||||||
|
UPDATEDPS = "updatedps" # Request refresh of DPS
|
||||||
|
|
||||||
PROTOCOL_VERSION_BYTES_31 = b"3.1"
|
PROTOCOL_VERSION_BYTES_31 = b"3.1"
|
||||||
PROTOCOL_VERSION_BYTES_33 = b"3.3"
|
PROTOCOL_VERSION_BYTES_33 = b"3.3"
|
||||||
@@ -76,6 +78,9 @@ SUFFIX_VALUE = 0x0000AA55
|
|||||||
|
|
||||||
HEARTBEAT_INTERVAL = 10
|
HEARTBEAT_INTERVAL = 10
|
||||||
|
|
||||||
|
# DPS that are known to be safe to use with update_dps (0x12) command
|
||||||
|
UPDATE_DPS_WHITELIST = [18, 19, 20] # Socket (Wi-Fi)
|
||||||
|
|
||||||
# This is intended to match requests.json payload at
|
# This is intended to match requests.json payload at
|
||||||
# https://github.com/codetheweb/tuyapi :
|
# https://github.com/codetheweb/tuyapi :
|
||||||
# type_0a devices require the 0a command as the status request
|
# type_0a devices require the 0a command as the status request
|
||||||
@@ -90,11 +95,13 @@ PAYLOAD_DICT = {
|
|||||||
STATUS: {"hexByte": 0x0A, "command": {"gwId": "", "devId": ""}},
|
STATUS: {"hexByte": 0x0A, "command": {"gwId": "", "devId": ""}},
|
||||||
SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}},
|
SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}},
|
||||||
HEARTBEAT: {"hexByte": 0x09, "command": {}},
|
HEARTBEAT: {"hexByte": 0x09, "command": {}},
|
||||||
|
UPDATEDPS: {"hexByte": 0x12, "command": {"dpId": [18, 19, 20]}},
|
||||||
},
|
},
|
||||||
"type_0d": {
|
"type_0d": {
|
||||||
STATUS: {"hexByte": 0x0D, "command": {"devId": "", "uid": "", "t": ""}},
|
STATUS: {"hexByte": 0x0D, "command": {"devId": "", "uid": "", "t": ""}},
|
||||||
SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}},
|
SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}},
|
||||||
HEARTBEAT: {"hexByte": 0x09, "command": {}},
|
HEARTBEAT: {"hexByte": 0x09, "command": {}},
|
||||||
|
UPDATEDPS: {"hexByte": 0x12, "command": {"dpId": [18, 19, 20]}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +299,8 @@ class MessageDispatcher(ContextualLogger):
|
|||||||
sem = self.listeners[self.HEARTBEAT_SEQNO]
|
sem = self.listeners[self.HEARTBEAT_SEQNO]
|
||||||
self.listeners[self.HEARTBEAT_SEQNO] = msg
|
self.listeners[self.HEARTBEAT_SEQNO] = msg
|
||||||
sem.release()
|
sem.release()
|
||||||
|
elif msg.cmd == 0x12:
|
||||||
|
self.debug("Got normal updatedps response")
|
||||||
elif msg.cmd == 0x08:
|
elif msg.cmd == 0x08:
|
||||||
self.debug("Got status update")
|
self.debug("Got status update")
|
||||||
self.listener(msg)
|
self.listener(msg)
|
||||||
@@ -478,6 +487,26 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger):
|
|||||||
"""Send a heartbeat message."""
|
"""Send a heartbeat message."""
|
||||||
return await self.exchange(HEARTBEAT)
|
return await self.exchange(HEARTBEAT)
|
||||||
|
|
||||||
|
async def update_dps(self, dps=None):
|
||||||
|
"""
|
||||||
|
Request device to update index.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dps([int]): list of dps to update, default=detected&whitelisted
|
||||||
|
"""
|
||||||
|
if self.version == 3.3:
|
||||||
|
if dps is None:
|
||||||
|
if not self.dps_cache:
|
||||||
|
await self.detect_available_dps()
|
||||||
|
if self.dps_cache:
|
||||||
|
dps = [int(dp) for dp in self.dps_cache]
|
||||||
|
# filter non whitelisted dps
|
||||||
|
dps = list(set(dps).intersection(set(UPDATE_DPS_WHITELIST)))
|
||||||
|
self.debug("updatedps() entry (dps %s, dps_cache %s)", dps, self.dps_cache)
|
||||||
|
payload = self._generate_payload(UPDATEDPS, dps)
|
||||||
|
self.transport.write(payload)
|
||||||
|
return True
|
||||||
|
|
||||||
async def set_dp(self, value, dp_index):
|
async def set_dp(self, value, dp_index):
|
||||||
"""
|
"""
|
||||||
Set value (may be any type: bool, int or string) of any dps index.
|
Set value (may be any type: bool, int or string) of any dps index.
|
||||||
@@ -582,7 +611,10 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger):
|
|||||||
json_data["t"] = str(int(time.time()))
|
json_data["t"] = str(int(time.time()))
|
||||||
|
|
||||||
if data is not None:
|
if data is not None:
|
||||||
json_data["dps"] = data
|
if "dpId" in json_data:
|
||||||
|
json_data["dpId"] = data
|
||||||
|
else:
|
||||||
|
json_data["dps"] = data
|
||||||
elif command_hb == 0x0D:
|
elif command_hb == 0x0D:
|
||||||
json_data["dps"] = self.dps_to_request
|
json_data["dps"] = self.dps_to_request
|
||||||
|
|
||||||
@@ -591,7 +623,7 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger):
|
|||||||
|
|
||||||
if self.version == 3.3:
|
if self.version == 3.3:
|
||||||
payload = self.cipher.encrypt(payload, False)
|
payload = self.cipher.encrypt(payload, False)
|
||||||
if command_hb != 0x0A:
|
if command_hb not in [0x0A, 0x12]:
|
||||||
# add the 3.3 header
|
# add the 3.3 header
|
||||||
payload = PROTOCOL_33_HEADER + payload
|
payload = PROTOCOL_33_HEADER + payload
|
||||||
elif command == SET:
|
elif command == SET:
|
||||||
|
@@ -4,10 +4,7 @@ from functools import partial
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant.components.select import DOMAIN, SelectEntity
|
from homeassistant.components.select import DOMAIN, SelectEntity
|
||||||
from homeassistant.const import (
|
from homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN
|
||||||
CONF_DEVICE_CLASS,
|
|
||||||
STATE_UNKNOWN,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .common import LocalTuyaEntity, async_setup_entry
|
from .common import LocalTuyaEntity, async_setup_entry
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
"device_id": "Device ID",
|
"device_id": "Device ID",
|
||||||
"local_key": "Local key",
|
"local_key": "Local key",
|
||||||
"protocol_version": "Protocol Version",
|
"protocol_version": "Protocol Version",
|
||||||
|
"scan_interval": "Scan interval (seconds, only when not updating automatically)",
|
||||||
"device_type": "Device type"
|
"device_type": "Device type"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -39,4 +40,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "LocalTuya"
|
"title": "LocalTuya"
|
||||||
}
|
}
|
||||||
|
@@ -48,7 +48,7 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity):
|
|||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def extra_state_attributes(self):
|
||||||
"""Return device state attributes."""
|
"""Return device state attributes."""
|
||||||
attrs = {}
|
attrs = {}
|
||||||
if self.has_config(CONF_CURRENT):
|
if self.has_config(CONF_CURRENT):
|
||||||
|
@@ -29,7 +29,8 @@
|
|||||||
"host": "Host",
|
"host": "Host",
|
||||||
"device_id": "Device ID",
|
"device_id": "Device ID",
|
||||||
"local_key": "Local key",
|
"local_key": "Local key",
|
||||||
"protocol_version": "Protocol Version"
|
"protocol_version": "Protocol Version",
|
||||||
|
"scan_interval": "Scan interval (seconds, only when not updating automatically)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pick_entity_type": {
|
"pick_entity_type": {
|
||||||
@@ -64,21 +65,21 @@
|
|||||||
"brightness_lower": "Brightness Lower Value",
|
"brightness_lower": "Brightness Lower Value",
|
||||||
"brightness_upper": "Brightness Upper Value",
|
"brightness_upper": "Brightness Upper Value",
|
||||||
"color_temp": "Color Temperature",
|
"color_temp": "Color Temperature",
|
||||||
|
"color_temp_reverse": "Color Temperature Reverse",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"color_mode": "Color Mode",
|
"color_mode": "Color Mode",
|
||||||
"color_temp_min_kelvin": "Minimum Color Temperature in K",
|
"color_temp_min_kelvin": "Minimum Color Temperature in K",
|
||||||
"color_temp_max_kelvin": "Maximum Color Temperature in K",
|
"color_temp_max_kelvin": "Maximum Color Temperature in K",
|
||||||
"music_mode": "Music mode available",
|
"music_mode": "Music mode available",
|
||||||
"scene": "Scene",
|
"scene": "Scene",
|
||||||
"fan_speed_control": "Fan Speed Control",
|
"fan_speed_control": "Fan Speed Control dps",
|
||||||
"fan_oscillating_control": "Fan Oscillating Control",
|
"fan_oscillating_control": "Fan Oscillating Control dps",
|
||||||
"fan_speed_low": "Fan Low Speed Setting",
|
"fan_speed_min": "minimum fan speed integer",
|
||||||
"fan_speed_medium": "Fan Medium Speed Setting",
|
"fan_speed_max": "maximum fan speed integer",
|
||||||
"fan_speed_high": "Fan High Speed Setting",
|
"fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)",
|
||||||
"max_value": "Maximum Value",
|
"fan_direction":"fan direction dps",
|
||||||
"min_value": "Minimum Value",
|
"fan_direction_forward": "forward dps string",
|
||||||
"select_options": "Valid entries, separate entries by a ;",
|
"fan_direction_reverse": "reverse dps string",
|
||||||
"select_options_friendly": "User Friendly options, separate entries by a ;",
|
|
||||||
"current_temperature_dp": "Current Temperature",
|
"current_temperature_dp": "Current Temperature",
|
||||||
"target_temperature_dp": "Target Temperature",
|
"target_temperature_dp": "Target Temperature",
|
||||||
"temperature_step": "Temperature Step (optional)",
|
"temperature_step": "Temperature Step (optional)",
|
||||||
@@ -110,6 +111,7 @@
|
|||||||
"host": "Host",
|
"host": "Host",
|
||||||
"local_key": "Local key",
|
"local_key": "Local key",
|
||||||
"protocol_version": "Protocol Version",
|
"protocol_version": "Protocol Version",
|
||||||
|
"scan_interval": "Scan interval (seconds, only when not updating automatically)",
|
||||||
"entities": "Entities (uncheck an entity to remove it)"
|
"entities": "Entities (uncheck an entity to remove it)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -137,21 +139,21 @@
|
|||||||
"brightness_lower": "Brightness Lower Value",
|
"brightness_lower": "Brightness Lower Value",
|
||||||
"brightness_upper": "Brightness Upper Value",
|
"brightness_upper": "Brightness Upper Value",
|
||||||
"color_temp": "Color Temperature",
|
"color_temp": "Color Temperature",
|
||||||
|
"color_temp_reverse": "Color Temperature Reverse",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"color_mode": "Color Mode",
|
"color_mode": "Color Mode",
|
||||||
"color_temp_min_kelvin": "Minimum Color Temperature in K",
|
"color_temp_min_kelvin": "Minimum Color Temperature in K",
|
||||||
"color_temp_max_kelvin": "Maximum Color Temperature in K",
|
"color_temp_max_kelvin": "Maximum Color Temperature in K",
|
||||||
"music_mode": "Music mode available",
|
"music_mode": "Music mode available",
|
||||||
"scene": "Scene",
|
"scene": "Scene",
|
||||||
"fan_speed_control": "Fan Speed Control",
|
"fan_speed_control": "Fan Speed Control dps",
|
||||||
"fan_oscillating_control": "Fan Oscillating Control",
|
"fan_oscillating_control": "Fan Oscillating Control dps",
|
||||||
"fan_speed_low": "Fan Low Speed Setting",
|
"fan_speed_min": "minimum fan speed integer",
|
||||||
"fan_speed_medium": "Fan Medium Speed Setting",
|
"fan_speed_max": "maximum fan speed integer",
|
||||||
"fan_speed_high": "Fan High Speed Setting",
|
"fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)",
|
||||||
"max_value": "Maximum Value",
|
"fan_direction":"fan direction dps",
|
||||||
"min_value": "Minimum Value",
|
"fan_direction_forward": "forward dps string",
|
||||||
"select_options": "Valid entries, separate entries by a ;",
|
"fan_direction_reverse": "reverse dps string",
|
||||||
"select_options_friendly": "User Friendly options, separate entries by a ;",
|
|
||||||
"current_temperature_dp": "Current Temperature",
|
"current_temperature_dp": "Current Temperature",
|
||||||
"target_temperature_dp": "Target Temperature",
|
"target_temperature_dp": "Target Temperature",
|
||||||
"temperature_step": "Temperature Step (optional)",
|
"temperature_step": "Temperature Step (optional)",
|
||||||
|
12
info.md
12
info.md
@@ -43,6 +43,8 @@ localtuya:
|
|||||||
local_key: xxxxx
|
local_key: xxxxx
|
||||||
friendly_name: Tuya Device
|
friendly_name: Tuya Device
|
||||||
protocol_version: "3.3"
|
protocol_version: "3.3"
|
||||||
|
scan_interval: # optional, only needed if energy monitoring values are not updating
|
||||||
|
seconds: 30 # Values less than 10 seconds may cause stability issues
|
||||||
entities:
|
entities:
|
||||||
- platform: binary_sensor
|
- platform: binary_sensor
|
||||||
friendly_name: Plug Status
|
friendly_name: Plug Status
|
||||||
@@ -109,9 +111,12 @@ select one of these, or manually input all the parameters.
|
|||||||

|

|
||||||
|
|
||||||
If you have selected one entry, you just have to input the Friendly Name of the Device, and the localKey.
|
If you have selected one entry, you just have to input the Friendly Name of the Device, and the localKey.
|
||||||
Once you press "Submit", the connection will be tested to check that everything works, in order to proceed.
|
|
||||||
|
|
||||||

|
Setting the scan interval is optional, only needed if energy/power values are not updating frequently enough by default. Values less than 10 seconds may cause stability issues.
|
||||||
|
|
||||||
|
Once you press "Submit", the connection is tested to check that everything works.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
Then, it's time to add the entities: this step will take place several times. Select the entity type from the drop-down menu to set it up.
|
Then, it's time to add the entities: this step will take place several times. Select the entity type from the drop-down menu to set it up.
|
||||||
After you have defined all the needed entities leave the "Do not add more entities" checkbox checked: this will complete the procedure.
|
After you have defined all the needed entities leave the "Do not add more entities" checkbox checked: this will complete the procedure.
|
||||||
@@ -133,7 +138,8 @@ After all the entities have been configured, the procedure is complete, and the
|
|||||||
|
|
||||||
Energy monitoring (voltage, current...) values can be obtained in two different ways:
|
Energy monitoring (voltage, current...) values can be obtained in two different ways:
|
||||||
1) creating individual sensors, each one with the desired name. Note: Voltage and Consumption usually include the first decimal, so 0.1 as "scaling" parameter shall be used in order to get the correct values.
|
1) creating individual sensors, each one with the desired name. Note: Voltage and Consumption usually include the first decimal, so 0.1 as "scaling" parameter shall be used in order to get the correct values.
|
||||||
2) accessing the voltage/current/current_consumption attributes of a switch, and then defining template sensors like this (please note that in this case the values are already divided by 10 for Voltage and Consumption):
|
2) accessing the voltage/current/current_consumption attributes of a switch, and then defining template sensors like this (please note that in this case the values are already divided by 10 for Voltage and Consumption)
|
||||||
|
3) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended.
|
||||||
|
|
||||||
```
|
```
|
||||||
sensor:
|
sensor:
|
||||||
|
Reference in New Issue
Block a user