فهرست منبع

Add support for HIRCIR IR remote controller.

This remote has separate dps for all the send parameters.
Add in type and delay dps handling, to match the data sent in JSON for
single dp devices.

Issue #1215
Jason Rumney 2 سال پیش
والد
کامیت
f844c62710

+ 82 - 85
custom_components/tuya_local/devices/README.md

@@ -14,7 +14,7 @@ The top level of the device configuration defines the following:
 ### `name`
 
 The device should be named descriptively with a name the user would recognize,
-the brand and model of the device is a good choice.  If a whole family of
+the brand and model of the device is a good choice. If a whole family of
 devices is supported, a generalization of the model type can be used.
 The name should also indicate to the user what type of device it is.
 
@@ -22,13 +22,13 @@ The name should also indicate to the user what type of device it is.
 
 *Optional, for future use.*
 
-A list of products that this config applies to.  Each product in the list must
+A list of products that this config applies to. Each product in the list must
 have an `id` specified, which corresponds to the productId or productKey
-(depending on where you are getting it from) in Tuya info.  This is available
+(depending on where you are getting it from) in Tuya info. This is available
 from the Tuya developer web portal listing for your device, or when using
-UDP discovery (via tinytuya).  In future it is intended that UDP discovery
+UDP discovery (via tinytuya). In future it is intended that UDP discovery
 will be used to more precisely match devices to configs, so it is recommended
-to report these if you can find them when requesting a new device.  Each
+to report these if you can find them when requesting a new device. Each
 listing can also have an optional `name`, which is intended to override the
 top level `name` when full support for this field is added.
 Probably other info will be added in future to provide better reporting of
@@ -59,10 +59,10 @@ and is detailed in the section below.
 
 ### `entity`
 
-The Home Assistant entity type being configured.  Currently supported
+The Home Assistant entity type being configured. Currently supported
 types are **climate**, **switch**, **light**, **lock**. Functionality
 for these entities is limited to that which has been required for the
-devices until now and may need to be extended for new devices.  In
+devices until now and may need to be extended for new devices. In
 particular, the light and lock entities have only been used for simple
 secondary entities, so only basic functionality is implemented.
 
@@ -71,7 +71,7 @@ secondary entities, so only basic functionality is implemented.
 *Optional.*
 
 For some entity types, a device `class` can be set, for example `switch`
-entities can have a class of `outlet`.  This may slightly alter the UI
+entities can have a class of `outlet`. This may slightly alter the UI
 behaviour.
 For most entities, it will alter the default icon, and for binary sensors
 also the state that off and on values translate to in the UI.
@@ -80,14 +80,14 @@ also the state that off and on values translate to in the UI.
 
 *Optional.*
 
-This specifies the `entity category` of the entity.  Entities can be categorized
+This specifies the `entity category` of the entity. Entities can be categorized
 as `config` or `diagnostic` to restrict where they appear automatically in
 Home Assistant.
 
 ### `dps`
 
 This is a list of the definitions for the Tuya DPs associated with
-attributes of this entity.  There should be one list entry for each
+attributes of this entity. There should be one list entry for each
 supported DPs reported by the device.
 
 The configuration of DPs entries is detailed in its own section below.
@@ -104,10 +104,10 @@ name may not be sufficient to describe the function.
 
 ### `mode`
 
-*Optional.  For number entities, default="auto", for others, None*
+*Optional. For number entities, default="auto", for others, None*
 
 For number entities, this can be used to force `slider` or `box` as the
-input method.  The default `auto` uses a slider if the range is small enough,
+input method. The default `auto` uses a slider if the range is small enough,
 or a box otherwise.
 
 ## DPs configuration
@@ -125,15 +125,15 @@ The type of data returned by the Tuya API. Can be one of the following:
  - **integer** can contain only numbers. Integers can have range set on them, be scaled and steped
  - **bitfield** is a special case of integer, where the bits that make up the value each has individal meaning.
  - **unixtime** is a special case of integer, where the device uses a unix timestamp (seconds since 1970-01-01 00:00), which is converted to a datetime for Home Assistant
- - **base64** is a special case of string, where binary data is base64 encoded.  Platforms that use this type will need special handling to make sense of the data.
+ - **base64** is a special case of string, where binary data is base64 encoded. Platforms that use this type will need special handling to make sense of the data.
  - **hex** is a special case of string, where binary data is hex encoded. Platforms that use this type will need special handling to make sense of the data.
- - **json** is a special case of string, where multiple data points are encoded in json format in the string.  Platforms that use this type will need special handling to make sense of the data.
- - **float** can contain floating point numbers.  No known devices use this, but it is supported if needed.
+ - **json** is a special case of string, where multiple data points are encoded in json format in the string. Platforms that use this type will need special handling to make sense of the data.
+ - **float** can contain floating point numbers. No known devices use this, but it is supported if needed.
 
 ### `name`
 
 The name given to the attribute in Home Assistant. Certain names are used
-by the Home Assistant entities for specific purposes.  If a name is not
+by the Home Assistant entities for specific purposes. If a name is not
 recognized as a standard attribute by the entitiy implementation, the
 attribute will be returned as a readonly custom attribute on the entity.
 If you need non-standard attributes to be able to be set, you will need
@@ -144,17 +144,17 @@ to use a secondary entity for that.
 *Optional, default false.*
 
 A boolean setting to mark attributes as readonly. If not specified, the
-default is `false`.  If set to `true`, the attributes will be reported
+default is `false`. If set to `true`, the attributes will be reported
 to Home Assistant, but attempting to set them will result in an error.
 This is only needed in contexts where it would normally be possible to set
-the value.  If you are creating a sensor entity, or adding an attribute of an
+the value. If you are creating a sensor entity, or adding an attribute of an
 entity which is inherently read-only, then you do not need to specify this.
 
 ### `optional`
 
 *Optional, default false.*
 
-A boolean setting to mark attributes as optional.  This allows a device to be
+A boolean setting to mark attributes as optional. This allows a device to be
 matched even if it is not sending the dp at the time when adding a new device.
 It can also be used to match a range of devices that have variations in the extra
 attributes that are sent.
@@ -164,10 +164,10 @@ attributes that are sent.
 *Optional, default true.*
 
 Whether to persist the value if the device does not return it on every status
-refresh.  Some devices don't return every value on every status poll. In most
+refresh. Some devices don't return every value on every status poll. In most
 cases, it is better to remember the previous value, but in some cases the
 dp is used to signal an event, so when it is next sent, it should trigger
-automations even if it is the same value as previously sent.  In that case
+automations even if it is the same value as previously sent. In that case
 the value needs to go to null in between when the device is not sending it.
 
 ### `force`
@@ -175,17 +175,17 @@ the value needs to go to null in between when the device is not sending it.
 *Optional, default false.*
 
 A boolean setting to mark dps as requiring an explicit update request
-to fetch.  Many energy monitoring smartplugs require this, without a
+to fetch. Many energy monitoring smartplugs require this, without a
 explicit request to update them, such plugs will only return monitoring data
-rarely or never.  Devices can misbehave if this is used on dps that do not
-require it.  Use this only where needed, and generally only on read-only dps.
+rarely or never. Devices can misbehave if this is used on dps that do not
+require it. Use this only where needed, and generally only on read-only dps.
 
 ### `precision`
 
 *Optional, default None.*
 
 For integer dps that are sensor values, the suggested precision for
-display in Home Assistant can be specified.  If unspecified, the Home
+display in Home Assistant can be specified. If unspecified, the Home
 Assistant will use the native precision, which is calculated based on
 the scale of the dp so as to provide distinct values with as few
 decimal places as possible. For example a scale of 3 will result in
@@ -209,12 +209,12 @@ defined in their own section below.
 
 *Optional, default false.*
 This can be used to define DPs that do not directly expose Home Assistant
-attributes.  When set to **true**, no attribute will be sent. A `name` should
+attributes. When set to **true**, no attribute will be sent. A `name` should
 still be specified and the attribute can be referenced as a `constraint`
 from mapping rules on other attributes to implement complex mappings.
 
 An example of use is a climate device, where the Tuya device keeps separate
-temperature settings for different Normal and Eco preset modes.  The Normal
+temperature settings for different Normal and Eco preset modes. The Normal
 temperature setting is exposed through the standard `temperature`
 Home Assistant attribute on the climate device, but the `eco_temperature`
 setting on a different DP is set to hidden. Mapping Rules are used on the
@@ -227,14 +227,14 @@ is set to Eco.
 
 For integer attributes that are not readonly, a range can be set with `min`
 and `max` values that will limit the values that the user can enter in the
-Home Assistant UI.  This can also be set in a `mapping` or `conditions` block.
+Home Assistant UI. This can also be set in a `mapping` or `conditions` block.
 
 ### `unit`
 
 *Optional, default="C" for temperature dps on climate devices.*
 
-For temperature dps, some devices will use Fahrenhiet.  This needs to be
-indicated back to HomeAssistant by defining `unit` as "F".  For sensor
+For temperature dps, some devices will use Fahrenhiet. This needs to be
+indicated back to HomeAssistant by defining `unit` as "F". For sensor
 entities, see the HomeAssistant developer documentation for the full list
 of possible units (C and F are automatically translated to their Unicode
 equivalents, other units are currently ASCII so can be easily entered directly).
@@ -252,14 +252,14 @@ or total_increasing)
 *Optional.*
 
 For base64 and hex types, this specifies how to decode the binary data (after hex or base64 decoding).
-This is a container field, the contents of which should be a list consisting of `name`, `bytes` and `range` fields.  `range` is as described above.  `bytes` is the number of bytes for the field, which can be `1`, `2`, or `4`.  `name` is a name for the field, which will have special handling depending on
+This is a container field, the contents of which should be a list consisting of `name`, `bytes` and `range` fields. `range` is as described above. `bytes` is the number of bytes for the field, which can be `1`, `2`, or `4`. `name` is a name for the field, which will have special handling depending on
 the device type.
 
 ### `mask`
 
 *Optional.*
 
-For base64 and hex types, this specifies how to extract a single numeric value from the binary data.  The value should be a hex bit mask (eg 00FF00 to extract the middle byte of a 3 byte value).  Unlike format, this does not require special handling in the entity platform, as only a single value is being extracted.
+For base64 and hex types, this specifies how to extract a single numeric value from the binary data. The value should be a hex bit mask (eg 00FF00 to extract the middle byte of a 3 byte value). Unlike format, this does not require special handling in the entity platform, as only a single value is being extracted.
 
 ### `endianness`
 
@@ -270,10 +270,10 @@ For base64 and hex types, this specifies the endianess of the data and mask. Cou
 ## Mapping Rules
 
 Mapping rules can change the behavior of attributes beyond simple
-copying of DP values to attribute values.  Rules can be defined
+copying of DP values to attribute values. Rules can be defined
 without a dps_val to apply to all values, or a list of rules that
 apply to particular dp values can be defined to change only
-particular cases.  Rules can even depend on the values of other
+particular cases. Rules can even depend on the values of other
 elements.
 
 ### `dps_val`
@@ -284,15 +284,15 @@ values not covered by their own dps_val rule.*
 `dps_val` defines the DP value that each
 rule in the list applies to. This can be used to map specific values from the
 Tuya protocol into attribute values that have specific meaning in Home
-Assistant.  For example, climate entities in Home Assistant define modes
+Assistant. For example, climate entities in Home Assistant define modes
 "off", "heat", "cool", "heat_cool", "auto" and "dry". But in the Tuya protocol,
-a simple heater just has a boolean off/on switch.  It can also be used to
-change the icon when a specific mode is operational.  For example if
+a simple heater just has a boolean off/on switch. It can also be used to
+change the icon when a specific mode is operational. For example if
 a heater device has a fan-only mode, you could change the icon to "mdi:fan"
 instead of "mdi:radiator" when in that mode.
 A `dps_val` of `null` can be used to specify a value to be assumed when a
 dp is not being returned by the device, to avoid None in some locations where
-that causes an issue such as entities showing as unavailable.  Such a mapping
+that causes an issue such as entities showing as unavailable. Such a mapping
 is one-way, the value will not be mapped back to a null when setting the dp.
 
 ### `value`
@@ -300,9 +300,9 @@ is one-way, the value will not be mapped back to a null when setting the dp.
 *Optional.*
 
 This can be used to set the attribute value seen by Home Assistant to something
-different than the DP value from the Tuya protocol.  Normally it will be used
+different than the DP value from the Tuya protocol. Normally it will be used
 with `dps_val` to map from one value to another. Without `dps_val` it will
-one-way map all otherwise unmapped dps values to the specified value.  This
+one-way map all otherwise unmapped dps values to the specified value. This
 can be useful for a binary_sensor.
 
 ### `hidden`
@@ -311,7 +311,7 @@ can be useful for a binary_sensor.
 
 When set to true, the mapping value is hidden from the list of all values.
 This can be used for items that should not be available for selection by the
-user but you still want to map for feedback coming from the device.  For
+user but you still want to map for feedback coming from the device. For
 example, some devices have a "Manual" mode, which is automatically selected
 when adjustments are made to other settings, but should not be available as
 an explicit mode for the user to select.
@@ -320,10 +320,10 @@ an explicit mode for the user to select.
 
 *Optional, default=1.*
 
-This can be used in an `integer` dp mapping to scale the values.  For example
+This can be used in an `integer` dp mapping to scale the values. For example
 some climate devices represent the temperature as an integer in tenths of
 degrees, and require a scale of 10 to convert them to degrees expected by
-Home Assistant.  The scale can also be the other way, for a fan with speeds
+Home Assistant. The scale can also be the other way, for a fan with speeds
 1, 2 and 3 as DP values, this can be converted to a percentage with a scale
 of 0.03.
 
@@ -331,9 +331,9 @@ of 0.03.
 
 *Optional, default=False.*
 
-This can be used in an `integer` dp mapping to invert the range.  For example,
+This can be used in an `integer` dp mapping to invert the range. For example,
 some cover devices have an opposite idea of which end of the percentage scale open
-and closed are from what Home Assistant assumes.  To use this mapping option, a range
+and closed are from what Home Assistant assumes. To use this mapping option, a range
 must also be specified for the dp.
 
 ### `step`
@@ -341,8 +341,8 @@ must also be specified for the dp.
 *Optional, default=1.*
 
 This can be used in an `integer` dp mapping to make values jump by a specific
-step.  It can also be set in a conditions block so that the steps change only
-under certain conditions.  An example is where a value has a range of 0-100, but
+step. It can also be set in a conditions block so that the steps change only
+under certain conditions. An example is where a value has a range of 0-100, but
 only allows settings that are divisible by 10, so a step of 10 would be set.
 
 ### `target_range`
@@ -350,8 +350,8 @@ only allows settings that are divisible by 10, so a step of 10 would be set.
 *Optional, has `min` and `max` child attributes, like `range`*
 
 A target range is used together with `range` on a numeric value, to
-map the value into a new range.  Unlike `scale`, this can shift the
-value as well as scale it into the new range.  Color temperature is a
+map the value into a new range. Unlike `scale`, this can shift the
+value as well as scale it into the new range. Color temperature is a
 major use of this, as Tuya devices often use a range of 0 - 100, 0 -
 255 or 0 - 1000, and this needs to be mapped to the Kelvin like 2200 -
 6500.
@@ -365,7 +365,7 @@ across the range.
 
 *Optional.*
 
-This can be used to override the icon.  Most useful with a `dps_val` which
+This can be used to override the icon. Most useful with a `dps_val` which
 indicates a change from normal operating mode, such as "fan-only",
 "defrosting", "tank-full" or some error state.
 
@@ -374,9 +374,9 @@ indicates a change from normal operating mode, such as "fan-only",
 *Optional. Default 10. Lower numbers mean higher priorities.*
 
 When a number of rules on different attributes define `icon` changes, you
-may need to control which have priority over the others.  For example,
+may need to control which have priority over the others. For example,
 if the device is off, probably it is more important to indicate that than
-whether it is in fan-only or heat mode.  So in the off/on DP, you might
+whether it is in fan-only or heat mode. So in the off/on DP, you might
 give a priority of 1 to the off icon, 3 to the on icon, and in the mode DP
 you could give a priority of 2 to the fan icon, to make it override the
 normal on icon, but not the off icon.
@@ -393,7 +393,7 @@ set it will be redirected to the named attribute instead of the current one.
 
 An example of how this can be useful is where a Tuya heater has a dp for the
 target temperature in normal mode, and a different dp for the target
-temperature is "eco" mode.  Depending on the `preset_mode`, you need to use
+temperature is "eco" mode. Depending on the `preset_mode`, you need to use
 one or the other. But Home Assistant just has one `temperature` attribute for
 setting target temperature, so the mapping needs to be done before passing to
 Home Assistant.
@@ -403,13 +403,13 @@ Home Assistant.
 *Optional.*
 
 When `value_mirror` is set, the value of the attribute will be redirected to
-the current value of the named attribute.  Unlike `value_redirect`, this does
+the current value of the named attribute. Unlike `value_redirect`, this does
 not redirect attempts to set the dp to the redirected dp, but when used in
 a map, this can make the mapping dynamic.
 
 An example of how this can be useful is where a thermostat can be configured
 to control either a heating or cooling device, but it is not expected to
-change this setting during operation.  Once set up, the hvac_mode dp can
+change this setting during operation. Once set up, the hvac_mode dp can
 have a mapping that mirrors the value of the configuration dp.
 
 ### `invalid`
@@ -417,11 +417,11 @@ have a mapping that mirrors the value of the configuration dp.
 *Optional, default false.*
 
 Invalid set to true allows an attribute to temporarily be set read-only in
-some conditions.  Rather than passing requests to set the attribute through
+some conditions. Rather than passing requests to set the attribute through
 to the Tuya protocol, attempts to set it will throw an error while it meets
-the conditions to be `invalid`.  It does not make sense to set this at mapping
+the conditions to be `invalid`. It does not make sense to set this at mapping
 level, as it would cause a situation where you can set a value then not be
-able to unset it.  Instead, this should be used with conditions, below, to
+able to unset it. Instead, this should be used with conditions, below, to
 make the behaviour dependent on another DP, such as disabling fan speed
 control when the preset is in sleep mode (since sleep mode should force low).
 
@@ -438,16 +438,16 @@ pick a default tone to use to turn on the siren.
 
 ### `constraint`
 
-*Optional, always paired with `conditions`.  Default if unspecified is the current attribute*
+*Optional, always paired with `conditions`. Default if unspecified is the current attribute*
 
 If a rule depends on an attribute other than the current one, then `constraint`
-can be used to specify the element that `conditions` applies to.  `constraint` can also refer back to the same attribute - this can be useful for specifying conditional mappings, for example to support two different variants of a device in a single config file, where the only difference is the way they represent enum attributes.
+can be used to specify the element that `conditions` applies to. `constraint` can also refer back to the same attribute - this can be useful for specifying conditional mappings, for example to support two different variants of a device in a single config file, where the only difference is the way they represent enum attributes.
 
 ### `conditions`
 
 *Optional, usually paired with `constraint.`*
 
-Conditions defines a list of rules that are applied based on the `constraint` attribute. The contents are the same as Mapping Rules, but `dps_val` applies to the attribute specified by `constraint`, and also can be a list of values to match as well rather than a single value.  All others act on the current attribute as they would in the mapping.  Although conditions are specified within a mapping, they can also contain a `mapping` of their own to override that mapping.  These nested mappings are limited to simple `dps_val` to `value` substitutions, as more complex rules would quickly become too complex to manage.
+Conditions defines a list of rules that are applied based on the `constraint` attribute. The contents are the same as Mapping Rules, but `dps_val` applies to the attribute specified by `constraint`, and also can be a list of values to match as well rather than a single value. All others act on the current attribute as they would in the mapping. Although conditions are specified within a mapping, they can also contain a `mapping` of their own to override that mapping. These nested mappings are limited to simple `dps_val` to `value` substitutions, as more complex rules would quickly become too complex to manage.
 
 When setting a dp which has conditions attached, the behaviour is slightly different depending on whether the constraint dp is readonly or not.
 
@@ -507,32 +507,28 @@ If `constraint_dp` is readonly:
 
 ## Entity types
 
-Entities have specific mappings of dp names to functions.  Any unrecognized dp name is added
-to the entity as a read-only extra attribute, so can be observed and queried from HA, but if you need
-to be able to change it, you should split it into its own entity of an appropriate type (number, select, switch for example).
+Entities have specific mappings of dp names to functions. Any unrecognized dp name is added to the entity as a read-only extra attribute, so can be observed and queried from HA, but if you need to be able to change it, you should split it into its own entity of an appropriate type (number, select, switch for example).
 
-If the type of dp does not match the expected type, a mapping should be provided to convert.
-Note that "on" and "off" require quotes in yaml, otherwise it they are interpretted as true/false.
+If the type of dp does not match the expected type, a mapping should be provided to convert. Note that "on" and "off" require quotes in yaml, otherwise it they are interpretted as true/false.
 
-Many entity types support a class attribute which may change the UI behaviour, icons etc.  See the
-HA documentation for the entity type to see what is valid (these may expand over time)
+Many entity types support a class attribute which may change the UI behaviour, icons etc. See the HA documentation for the entity type to see what is valid (these may expand over time)
 
 ### `alarm_control_panel`
-- **alarm_state** (required, string) the alarm state, used to report and change the current state of the alarm. Expects values from the set `disarmed`, `armed_home`, `armed_away`, `armed_night`, `armed_vacation`, `armed_custom_bypass`, `pending`, `arming`, `disarming`, `triggered`.  Other states are allowed for read-only status, but only the armed... and disarmed states are available as commands.
+- **alarm_state** (required, string) the alarm state, used to report and change the current state of the alarm. Expects values from the set `disarmed`, `armed_home`, `armed_away`, `armed_night`, `armed_vacation`, `armed_custom_bypass`, `pending`, `arming`, `disarming`, `triggered`. Other states are allowed for read-only status, but only the armed... and disarmed states are available as commands.
 - **trigger** (optional, boolean) used to trigger the alarm remotely for test or panic button etc.
 
 ### `binary_sensor`
 - **sensor** (required, boolean) the dp to attach to the sensor.
 
 ### `button`
-- **button** (required, boolean) the dp to attach to the button.  Any
+- **button** (required, boolean) the dp to attach to the button. Any
 read value will be ignored, but the dp is expected to be present for
-device detection unless set to optional.  A value of true will be sent
+device detection unless set to optional. A value of true will be sent
 for a button press, map this to the desired dps_val if a different
 value is required.
 
 ### `climate`
-- **aux_heat** (optional, boolean, DEPRECATED) a dp to control the aux heat switch if the device has one. Note this is being deprecated by HA and no longer accessible from the UI since HA 2023.9, though the deprecation announcement is yet to be made as of 2023.11.  It is recommended not to use this, and instead use an separate switch entity.
+- **aux_heat** (optional, boolean, DEPRECATED) a dp to control the aux heat switch if the device has one. Note this is being deprecated by HA and no longer accessible from the UI since HA 2023.9, though the deprecation announcement is yet to be made as of 2023.11. It is recommended not to use this, and instead use an separate switch entity.
 - **current_temperature** (optional, number) a dp that reports the current temperature.
 - **current_humidity** (optional, number) a dp that reports the current humidity (%).
 - **fan_mode** (optional, mapping of strings) a dp to control the fan mode if available.
@@ -541,7 +537,7 @@ value is required.
 - **humidity** (optional, number) a dp to control the target humidity if available. (%)
 - **hvac_mode** (optional, mapping of strings) a dp to control the mode of the device.
     Possible values are: `"off", cool, heat, heat_cool, auto, dry, fan_only`
-- **hvac_action** (optional, string) a dp thar reports the current action of the device.
+- **hvac_action** (optional, string) a dp that reports the current action of the device.
     Possible values are: `"off", idle, cooling, heating, drying, fan`
 - **preset_mode** (optional, mapping of strings) a dp to control preset modes of the device.
    Any value is allowed, but HA has some standard presets:
@@ -557,7 +553,7 @@ value is required.
 - **temperature_unit** (optional, string) a dp that specifies the unit the device is configured for.
     Values should be mapped to "C" or "F" (case sensitive) - often the device will use a boolean or
 	lower case for this
-- **min_temperature** (optional, number) a dp that specifies the minimum temperature that can be set.   Some devices provide this, otherwise a fixed range on the temperature dp can be used.
+- **min_temperature** (optional, number) a dp that specifies the minimum temperature that can be set. Some devices provide this, otherwise a fixed range on the temperature dp can be used.
 - **max_temperature** (optional, number) a dp that specifies the maximum temperature that can be set.
 
 ### `cover`
@@ -575,7 +571,7 @@ Either **position** or **open** should be specified.
 ### `fan`
 - **switch** (optional, boolean): a dp to control the power state of the fan
 - **preset_mode** (optional, mapping of strings): a dp to control different modes of the fan.
-   Values `"off", low, medium, high` used to be handled specially by HA as deprecated speed aliases.  If these are the only "presets", consider mapping them as **speed** values instead, as voice assistants will respond to phrases like "turn the fan up/down" for speed.
+   Values `"off", low, medium, high` used to be handled specially by HA as deprecated speed aliases. If these are the only "presets", consider mapping them as **speed** values instead, as voice assistants will respond to phrases like "turn the fan up/down" for speed.
 - **speed** (optional, number 0-100): a dp to control the speed of the fan (%).
     scale and step can be used to convert smaller ranges to percentages, or a mapping for discrete values.
 - **oscillate** (optional, boolean): a dp to control whether the fan will oscillate or not.
@@ -591,23 +587,23 @@ Humidifer can also cover dehumidifiers (use class to specify which).
 - **current_humidity** (optional, number): a dp to report the current humidity measured by the device
 
 ### `lawn_mower`
-- **activity** (required, string): a dp to report the current activity of the mower.  Valid activities are `mowing`, `paused`, `docked`, `error` (from LawnMowerActivities in https://github.com/home-assistant/core/blob/dev/homeassistant/components/lawn_mower/const.py).  Any additional activities should be mapped to one of those, and exposed through an extra attribute or sensor entity that shows all the statuses that the mower is reporting.
+- **activity** (required, string): a dp to report the current activity of the mower. Valid activities are `mowing`, `paused`, `docked`, `error` (from LawnMowerActivities in https://github.com/home-assistant/core/blob/dev/homeassistant/components/lawn_mower/const.py). Any additional activities should be mapped to one of those, and exposed through an extra attribute or sensor entity that shows all the statuses that the mower is reporting.
 
-- **command** (required, string): a dp to send commands to the mower.  Recognised commands are `start_mowing`, `pause` and `dock`.  Any additional commands should be implemented via a `button` or `select` entity.
+- **command** (required, string): a dp to send commands to the mower. Recognised commands are `start_mowing`, `pause` and `dock`. Any additional commands should be implemented via a `button` or `select` entity.
 
 ### `light`
 - **switch** (optional, boolean): a dp to control the on/off state of the light
 - **brightness** (optional, number 0-255): a dp to control the dimmer if available.
-- **color_temp** (optional, number): a dp to control the color temperature if available.  See `target_range` above for mapping Tuya's range into Kelvin.
+- **color_temp** (optional, number): a dp to control the color temperature if available. See `target_range` above for mapping Tuya's range into Kelvin.
 
-- **rgbhsv** (optional, hex): a dp to control the color of the light, using encoded RGB and HSV values.  The `format` field names recognized for decoding this field are `r`, `g`, `b`, `h`, `s`, `v`.
+- **rgbhsv** (optional, hex): a dp to control the color of the light, using encoded RGB and HSV values. The `format` field names recognized for decoding this field are `r`, `g`, `b`, `h`, `s`, `v`.
 - **color_mode** (optional, mapping of strings): a dp to control which mode to use if the light supports multiple modes.
     Special values: `white, color_temp, hs, xy, rgb, rgbw, rgbww`, others will be treated as effects,
 	Note: only white, color_temp and hs are currently supported, others listed above are reserved and may be implemented in future when the need arises.
     If no `color_mode` dp is available, a single supported color mode will be
     calculated based on which of the above dps are available.
 - **effect** (optional, mapping of strings): a dp to control effects / presets supported by the light.
-   Note: If the light mixes in color modes in the same dp, `color_mode` should be used instead.  If the light contains both a separate dp for effects/scenes/presets and a mix of color_modes and effects (commonly scene and music) in the `color_mode` dp, then a separate select entity should be used for the dedicated dp to ensure the effects from `color_mode` are selectable.
+   Note: If the light mixes in color modes in the same dp, `color_mode` should be used instead. If the light contains both a separate dp for effects/scenes/presets and a mix of color_modes and effects (commonly scene and music) in the `color_mode` dp, then a separate select entity should be used for the dedicated dp to ensure the effects from `color_mode` are selectable.
 
 ### `lock`
 
@@ -642,9 +638,10 @@ no information will be available about which specific credential was used to unl
 
 ### `remote`
 - **send** (required, accepts a string): a dp to send remote codes.
-- **control** (optional, accepts strings `"send_ir"`, `"study"`, `"study_exit"`): a dp to send commands seperately from ir codes.  If not supplied, commands will be JSON formatted and sent through the **send** dp.
-- **receive** (optional, returns strings): a dp to receive learned commands on.  If not supplied, the `remote.learn_command` service call will not be available. 
-
+- **receive** (optional, returns strings): a dp to receive learned commands on. If not supplied, the `remote.learn_command` service call will not be available. 
+- **control** (optional, accepts strings `"send_ir"`, `"study"`, `"study_exit"`): a dp to send commands seperately from ir codes. If not supplied, commands will be JSON formatted and sent through the **send** dp.
+- **delay** (optional, accepts numbers): a dp to set the delay in ms between buttons when there are multiple in the send string. This is only used when a separate **control** dp is also supplied, otherwise the parameter is included in the JSON sent to the **send** dp.
+- **code_type** (optional, accepts integers): a dp to set the type of code being sent. The current implementation only supports type `0`. This is only used when a separate **control** dp is also supplied, otherwise the parameter is included in the JSON sent to the **send** dp.
 ### `select`
 - **option** (required, mapping of strings): a dp to control the option that is selected.
 
@@ -664,7 +661,7 @@ The value "off" will be used for turning off the siren, and will be filtered fro
 
 ### `vacuum`
 - **status** (required, mapping of strings): a dp to report and control the status of the vacuum.
-- **command** (optional, mapping of strings): a dp to control the statuss of the vacuum.  If supplied, the status dp is only used to report the state.
+- **command** (optional, mapping of strings): a dp to control the statuss of the vacuum. If supplied, the status dp is only used to report the state.
     Special values: `return_to_base, clean_spot`, others are sent as general commands
 - **locate** (optional, boolean): a dp to trigger a locator beep on the vacuum.
 - **power** (optional, boolean): a dp to switch full system power on and off
@@ -677,7 +674,7 @@ The value "off" will be used for turning off the siren, and will be filtered fro
 ### `water_heater`
 - **current_temperature** (optional, number): a dp that reports the current water temperature.
 
-- **operation_mode** (optional, mapping of strings): a dp to report and control the operation mode of the water heater.  If `away` is one of the modes, another mode must be marked as `default: true` to that the `away_mode_off` service knows which mode to switch out of away mode to.
+- **operation_mode** (optional, mapping of strings): a dp to report and control the operation mode of the water heater. If `away` is one of the modes, another mode must be marked as `default: true` to that the `away_mode_off` service knows which mode to switch out of away mode to.
 
 - **temperature** (optional, number): a dp to control the target water temperature of the water heater. A unit may be specified as an attribute if the `temperature_unit` dp is not available, otherwise the default of HA's current setting will be used.
 

+ 65 - 0
custom_components/tuya_local/devices/hircr_reemote_control.yaml

@@ -0,0 +1,65 @@
+name: IR remote control
+primary_entity:
+  entity: remote
+  dps:
+    - id: 1
+      type: string
+      name: control
+    - id: 2
+      type: string
+      optional: true
+      persist: false
+      name: receive
+    - id: 3
+      type: string
+      optional: true
+      name: send
+    - id: 4
+      type: string
+      optional: true
+      name: key_code
+    - id: 5
+      type: string
+      optional: true
+      name: key_code2
+    - id: 6
+      type: string
+      optional: true
+      name: key_code3
+    - id: 7
+      type: string
+      optional: true
+      name: key_study
+    - id: 8
+      type: string
+      optional: true
+      name: key_study2
+    - id: 9
+      type: string
+      optional: true
+      name: key_study3
+    - id: 10
+      type: integer
+      optional: true
+      name: delay
+      range:
+        min: 0
+        max: 65535
+      mapping:
+        - scale: 10
+      unit: ms
+    - id: 11
+      type: string
+      optional: true
+      name: key_code4
+    - id: 12
+      type: string
+      optional: true
+      name: key_study4
+    - id: 13
+      type: integer
+      optional: true
+      name: code_type
+      range:
+        min: 0
+        max: 255

+ 10 - 4
custom_components/tuya_local/remote.py

@@ -107,6 +107,8 @@ class TuyaLocalRemote(TuyaLocalEntity, RemoteEntity):
         self._receive_dp = dps_map.pop("receive", None)
         # Some remotes split out the control (command) into its own dp and just send raw codes in send
         self._control_dp = dps_map.pop("control", None)
+        self._delay_dp = dps_map.pop("delay", None)
+        self._type_dp = dps_map.pop("code_type", None)
         self._init_end(dps_map)
         self._attr_supported_features = 0
         if self._receive_dp:
@@ -170,7 +172,7 @@ class TuyaLocalRemote(TuyaLocalEntity, RemoteEntity):
             code_list.append(codes)
         return code_list
 
-    def _encode_send_code(self, code):
+    def _encode_send_code(self, code, delay):
         """Encode a remote command into dps values to send."""
         # Based on https://github.com/jasonacox/tinytuya/issues/74 and
         # the docs it references, there are two kinds of IR devices.
@@ -183,6 +185,10 @@ class TuyaLocalRemote(TuyaLocalEntity, RemoteEntity):
             # control and code are sent in seperate dps.
             dps = dps | self._control_dp.get_values_to_set(self._device, CMD_SEND)
             dps = dps | self._send_dp.get_values_to_set(self._device, code)
+            if self._delay_dp:
+                dps = dps | self._delay_dp.get_values_to_set(self._device, delay)
+            if self._type_dp:
+                dps = dps | self._type_dp.get_values_to_seet(self._device, 0)
         else:
             dps = dps | self._send_dp.get_values_to_set(
                 self._device,
@@ -193,7 +199,7 @@ class TuyaLocalRemote(TuyaLocalEntity, RemoteEntity):
                         # leading zero means use head, any other leeading character is discarded.
                         "key1": "1" + code,
                         "type": 0,
-                        "delay": 300,
+                        "delay": int(delay),
                     }
                 ),
             )
@@ -207,7 +213,7 @@ class TuyaLocalRemote(TuyaLocalEntity, RemoteEntity):
         commands = kwargs[ATTR_COMMAND]
         subdevice = kwargs.get(ATTR_DEVICE)
         repeat = kwargs.get(ATTR_NUM_REPEATS)
-        delay = kwargs.get(ATTR_DELAY_SECS)
+        delay = kwargs.get(ATTR_DELAY_SECS, DEFAULT_DELAY_SECS) * 1000
         service = f"{RM_DOMAIN}.{SERVICE_SEND_COMMAND}"
         if not self._storage_loaded:
             await self._async_load_storage()
@@ -228,7 +234,7 @@ class TuyaLocalRemote(TuyaLocalEntity, RemoteEntity):
             else:
                 code = codes[0]
 
-            dps_to_set = self._encode_send_code(code)
+            dps_to_set = self._encode_send_code(code, delay)
             await self._device.async_set_properties(dps_to_set)
 
             if len(codes) > 1: