Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
cover.py
Go to the documentation of this file.
1## @file
2## @brief ESPHome cover platform schema and code generation.
3## @ingroup hioc_codegen
4##
5## Bridges the YAML ``cover:`` platform declaration to the runtime IOHomeCover entity.
6
7import esphome.codegen as cg
8import esphome.config_validation as cv
9from esphome.components import button, cover, text_sensor
10from esphome.const import (
11 CONF_DISABLED_BY_DEFAULT,
12 CONF_ID,
13 CONF_NAME,
14 ENTITY_CATEGORY_DIAGNOSTIC,
15)
16from esphome.core import CORE, ID
17
18from . import (
19 home_io_control_ns,
20 IOHomeControlComponent,
21 CONF_HOME_IO_CONTROL_ID,
22 validate_device_id,
23 validate_status_poll_interval,
24 validate_device_type,
25 device_type_expression,
26)
27
28DEPENDENCIES = ["home_io_control"]
29
30CONF_DEVICE_ID = "io_device_id"
31CONF_INVERT_POSITION = "invert_position"
32CONF_LINKED_REMOTES = "linked_remotes"
33CONF_DEVICE_TYPE = "io_device_type"
34CONF_SUBTYPE = "io_subtype"
35CONF_STATUS_POLL_INTERVAL = "status_poll_interval"
36
37IOHomeCover = home_io_control_ns.class_("IOHomeCover", cover.Cover, cg.Component)
38IOHomeCoverFavoriteButton = home_io_control_ns.class_(
39 "IOHomeCoverFavoriteButton", button.Button, cg.Component
40)
41IOHomeDeviceNameTextSensor = home_io_control_ns.class_(
42 "IOHomeDeviceNameTextSensor", text_sensor.TextSensor, cg.Component
43)
44
45# Device types that support 0-100% position control (maps to DeviceCapabilityClass::COVER in C++).
46# Used to decide whether a favorite-position button companion should be generated.
47POSITION_CONTROL_DEVICE_TYPES = {
48 0x01, # venetian_blind
49 0x02, # roller_shutter
50 0x03, # awning
51 0x04, # window_opener
52 0x05, # garage_opener
53 0x07, # gate_opener
54 0x08, # rolling_door_opener
55 0x0A, # blind
56 0x0B, # screen
57 0x0D, # dual_shutter
58 0x10, # horizontal_awning
59 0x11, # external_venetian_blind
60 0x12, # louvre_blind
61 0x13, # curtain_track
62 0x18, # swinging_shutter
63}
64
65
67 """Check if the given device type value supports 0-100% position control."""
68 return value in POSITION_CONTROL_DEVICE_TYPES
69
70
71def favorite_button_id(parent_id):
72 """Generate a unique ID for the favorite-position button child entity."""
73 return ID(
74 f"{parent_id.id}_favorite_button",
75 is_declaration=True,
76 type=IOHomeCoverFavoriteButton,
77 )
78
79
81 """Derive the favorite-position button name from the parent cover name."""
82 base_name = config.get(CONF_NAME, "")
83 if base_name:
84 return f"{base_name} Favorite Position"
85 return "Favorite Position"
86
87
88def device_name_sensor_id(parent_id):
89 """Generate a unique ID for the diagnostic device-name text sensor."""
90 return ID(
91 f"{parent_id.id}_device_name_sensor",
92 is_declaration=True,
93 type=IOHomeDeviceNameTextSensor,
94 )
95
96
98 """Derive the device-name sensor name from the parent entity name."""
99 base_name = config.get(CONF_NAME, "")
100 if base_name:
101 return f"{base_name} Device Name"
102 return "Device Name"
103
104CONFIG_SCHEMA = (
105 cover.cover_schema(IOHomeCover)
106 .extend(
107 {
108 cv.GenerateID(CONF_HOME_IO_CONTROL_ID): cv.use_id(
109 IOHomeControlComponent
110 ),
111 cv.Required(CONF_DEVICE_ID): validate_device_id,
112 cv.Optional(CONF_INVERT_POSITION): cv.boolean,
113 cv.Optional(CONF_DEVICE_TYPE): validate_device_type,
114 cv.Optional(CONF_SUBTYPE): cv.int_range(min=0, max=63),
115 cv.Optional(CONF_LINKED_REMOTES): cv.ensure_list(validate_device_id),
116 cv.Optional(CONF_STATUS_POLL_INTERVAL): validate_status_poll_interval,
117 }
118 )
119 .extend(cv.COMPONENT_SCHEMA)
120)
121
122
123async def to_code(config):
124 var = await cover.new_cover(config)
125 await cg.register_component(var, config)
126
127 parent = await cg.get_variable(config[CONF_HOME_IO_CONTROL_ID])
128 cg.add(var.set_parent(parent))
129 cg.add(var.set_device_id(config[CONF_DEVICE_ID]))
130
131 if CONF_INVERT_POSITION in config:
132 cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
133
134 if CONF_DEVICE_TYPE in config:
135 cg.add(var.set_device_type(device_type_expression(config[CONF_DEVICE_TYPE])))
136 if CONF_SUBTYPE in config:
137 cg.add(var.set_subtype(config[CONF_SUBTYPE]))
138 if CONF_STATUS_POLL_INTERVAL in config:
139 cg.add(var.set_status_poll_interval(config[CONF_STATUS_POLL_INTERVAL].total_milliseconds))
140
141 if CONF_LINKED_REMOTES in config:
142 for remote_id in config[CONF_LINKED_REMOTES]:
143 cg.add(parent.add_linked_remote(remote_id, config[CONF_DEVICE_ID]))
144
145 if CONF_DEVICE_TYPE in config and device_supports_position_control(
146 config[CONF_DEVICE_TYPE]
147 ):
148 favorite_config = {
149 CONF_ID: favorite_button_id(config[CONF_ID]),
150 CONF_NAME: favorite_button_name(config),
151 CONF_DISABLED_BY_DEFAULT: False,
152 }
153 favorite = await button.new_button(favorite_config)
154 CORE.component_ids.add(str(favorite.base))
155 await cg.register_component(favorite, favorite_config)
156 cg.add(favorite.set_parent(parent))
157 cg.add(favorite.set_device_id(config[CONF_DEVICE_ID]))
158
159 device_name_config = {
160 CONF_ID: device_name_sensor_id(config[CONF_ID]),
161 CONF_NAME: device_name_sensor_name(config),
162 CONF_DISABLED_BY_DEFAULT: True,
163 "entity_category": ENTITY_CATEGORY_DIAGNOSTIC,
164 }
165 device_name = await text_sensor.new_text_sensor(device_name_config)
166 CORE.component_ids.add(str(device_name.base))
167 await cg.register_component(device_name, device_name_config)
168 cg.add(device_name.set_parent(parent))
169 cg.add(device_name.set_device_id(config[CONF_DEVICE_ID]))
favorite_button_id(parent_id)
Definition cover.py:71
device_name_sensor_name(config)
Definition cover.py:97
device_supports_position_control(value)
Definition cover.py:66
device_name_sensor_id(parent_id)
Definition cover.py:88
favorite_button_name(config)
Definition cover.py:80
device_type_expression(value)
Definition __init__.py:107