-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathiron timer Mk2.yaml
executable file
·305 lines (291 loc) · 12 KB
/
iron timer Mk2.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#******************************************************************************
# ESPHome based Iron Timer
# Never forget to turn your clothes iron off again!
# Clothes iron is connected to a WiFi power switch controlled by Home Assistant (HA).
# A rotary encoder allows user to input countdown time, when the timer finishes, sends a message to
# HA to switch off the iron. A 14-segment alphanumeric LED display shows the selected and
# remaining time.
#
# Function:
# 1. Use a rotary encoder to select number of minutes on LED display. Starts at 5:00 (m:ss). Adjusts in 15s increments. Max 7:00.
# 2. Press button on rotary encoder to start countdown timer.
# 3. When countdown finishes, HA switches the iron off.
# 4. LED display turns off and enters idle state.
# 5. Adjust LED display brightness at any time by press and hold encoder button for < 0.6seconds, then select beteween 0.1 and 1.0
# 6. To finish countdown early, double click encoder button. Switch turns off immeditely.
# 7. To add or remove time during countdown, simply twist the encoder in steps of 15s.
# 8. When device is idle, user can tap rotary encoder switch to wake up device, which resets to 5:00 minutes.
# 9. Display turns off (enters idle mode) if user does not perform an action wihtin 30 seconds.
#
# Code by Patrick Felstead
#******************************************************************************
substitutions:
# Replace with Home Assistant entity ID for WiFi switch that controls the iron
switch_name: switch.switch_iron
esphome:
name: iron_timer_mk2
platform: ESP32
board: esp32dev
includes:
- ./custom_components/Iron timer Mk2.h
on_boot:
then:
- sensor.rotary_encoder.set_value:
id: knob
value: !lambda 'return (float) id(countdown);'
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.0.25
gateway: 192.168.0.1
subnet: 255.255.255.0
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "IronTimer"
password: "Dzqy205310T2"
captive_portal:
# Enable logging
logger:
level: DEBUG
logs:
sensor: WARN
# Enable Home Assistant API
api:
ota:
# I2C pins for the HT16K33 I2C 4-digit LED display
i2c:
sda: GPIO21
scl: GPIO22
globals:
- id: led_brightness
type: float
initial_value: '0.3'
# Begin in Boot mode to display the project name
- id: rotary_encoder_mode
type: int
initial_value: '4'
# Set the default time period to 5:00 minutes (20 * 15 seconds = 300 seconds = 5:00 m:ss)
- id: countdown
type: int
restore_value: no
initial_value: '20'
- id: shutdown_early
type: bool
initial_value: 'false'
binary_sensor:
#******************************************************************************
# Push button on rotary encoder
# ESPHome docs: https://esphome.io/components/binary_sensor/index.html
#******************************************************************************
# This sensor is the push switch built into the shaft of the rotary encoder
# A short press wakes device from idle mode, or starts the countdown, or exits the set brightness mode
# A long press lets the user adjust the LED display brightness
# A double click during countdown will make the remaining time jump to near zero, i.e. the user wants to turn iron off immediately
#
# All modes are defined in "iron timer Mk2.h"
#******************************************************************************
- platform: gpio
internal: true
id: rotary_push
pin:
number: GPIO0
inverted: true
mode: INPUT_PULLUP
filters:
- delayed_on: 50ms # debounce
on_click:
- min_length: 50ms
max_length: 300ms
then:
- lambda: |-
// Turn display ON whenever user pushes the encoder button
id(led_display).set_brightness(id(led_brightness));
if (id(rotary_encoder_mode) == SET_TIMER_MODE) {
id(rotary_encoder_mode) = COUNTDOWN_MODE;
// set countdown time to current rotatry encoder
id(countdown) = (int) (id(knob).state * 15.0);
}
else if ((id(rotary_encoder_mode) == SET_BRIGHTNESS_MODE) || (id(rotary_encoder_mode) == IDLE_MODE)) {
id(rotary_encoder_mode) = SET_TIMER_MODE; // Enter 'set timer mode'
id(knob).set_value(20.0); // Set encoder initial value to 20 * 15 sec = 5 minutes
id(led_display).update();
}
- if:
condition:
# Turn the iron switch on when the countdown timer is first set to a non-zero value
lambda: 'return (id(rotary_encoder_mode) == COUNTDOWN_MODE) && (id(countdown) > 0);'
then:
- logger.log: "Timer started ==> Turn switch ON"
- homeassistant.service:
service: switch.turn_on
data:
entity_id: $switch_name
- min_length: 600ms
max_length: 2s
then:
- lambda: |-
// change mode to set LED display brightness
id(rotary_encoder_mode) = SET_BRIGHTNESS_MODE;
// Set brightness global and encoder to currently set brightness
float current_brightness = id(led_display).get_brightness();
current_brightness = round(current_brightness * 10.0);
id(knob).set_value(current_brightness);
id(led_display).update();
on_double_click:
then:
# If user wants to end countdown early, set count to almost zero so they see it finish counting for last few seconds
- lambda: |-
if (id(rotary_encoder_mode) == COUNTDOWN_MODE) {
id(shutdown_early) = true;
id(countdown) = 5; // 5 seconds remaining
id(led_display).update();
}
sensor:
#******************************************************************************
# Rotary encoder
# ESPHome docs: https://esphome.io/components/sensor/rotary_encoder.html
#******************************************************************************
- platform: rotary_encoder
name: "Rotary Encoder"
id: knob
pin_a: GPIO2
pin_b: GPIO4
resolution: 2
min_value: 0
# instead of usng a 'max_value', instead limit the max in lambda filter.
# This is so can adjust time upward during countdown when encoder just below max value
filters:
- debounce: 50ms # debounce the encoder wafers
- throttle: 100ms # limit updates if the user twists the encoder too fast :D
- lambda: |-
//ESP_LOGW("rotary", "rotary enc=%2.2f", x);
// Turn display ON whenever user moves the encoder - in case we're in idle mode
id(led_display).set_brightness(id(led_brightness));
if (id(rotary_encoder_mode) == SET_BRIGHTNESS_MODE) {
// Use the rotary encoder to set the brightness of the lcd_ht16k33 LED display [0.1 - 1.0]
if (x > 10.0)
x = 10.0;
else if (x < 1.0)
x = 1.0; // Prevent user selecting 0.0 as this turns display off
id(led_brightness) = x / 10.0;
id(led_display).set_brightness(id(led_brightness));
//ESP_LOGW("case 2: LED brightness", "brightness=%.1f", id(led_brightness));
}
else if ((id(rotary_encoder_mode) == COUNTDOWN_MODE) || (id(rotary_encoder_mode) == IDLE_MODE)) {
// While countdown is in progress, use rotary encoder to adjust the current countdown value (up or down)
x = round((float) id(countdown) / 15.0);
id(rotary_encoder_mode) = SET_TIMER_MODE;
}
// Limit max encoder to 28 * 15 = 420s = 7 minutes
// Use this limit insead of encoder platform 'max_value' because if encoder one click below max, won't trigger 'on_value' (ESPHome bug?)
if (x >= 28.0)
return 28.0;
else
return x;
on_value:
then:
# This code sets the encoder to the current countdown value returned by the above filter
# Not sure why this can't be done in the filter lambda above, but it gets ignored there (compiles ok)
# i.e. the 'on_value' is triggered by the new filtered value above, but has not been written to the current encoder value for subsequent encoder movement
- sensor.rotary_encoder.set_value:
id: knob
value: !lambda 'return x;'
# Force display to update immediately, instead of waiting 1sec for the display update_interval
- component.update: led_display
display:
- platform: lcd_ht16k33
address: 0x70
update_interval: 1s
scroll: true
scroll_speed: 200ms
scroll_dwell: 10s
scroll_delay: 5.0
id: led_display
lambda: |-
int encoder = 0;
int mins = 0;
int secs = 0;
//ESP_LOGW("display", "Mode = %d", id(rotary_encoder_mode));
switch (id(rotary_encoder_mode)) {
case SET_TIMER_MODE:
encoder = (int) (id(knob).state * 15.0); // Display in 15 second increments
// Set countdown timer - just display, don't countdown
secs = encoder % 60;
mins = (encoder - secs) / 60;
if (encoder > 0)
it.printf("%2d.%02d", mins, secs);
break;
case COUNTDOWN_MODE:
if (id(countdown) > 0 ) {
id(countdown)--;
secs = id(countdown) % 60;
mins = (id(countdown) - secs) / 60;
if (!id(shutdown_early))
it.printf("%2d.%02d", mins, secs);
else {
// Put an asterix in the first digit as another cue to the user that they requested an immediate end to the countdown
it.printf("*%1d.%02d", mins, secs);
}
}
break;
case SET_BRIGHTNESS_MODE:
it.set_brightness(id(led_brightness));
it.printf("Brightness %.1f", id(led_brightness));
break;
case BOOT_MSG_MODE:
// This message gets displayed once for 5 seconds after power on
static int boot_timer = 0;
if (boot_timer <= 5) {
boot_timer++;
it.printf("%s", "Iron Timer");
}
else {
id(rotary_encoder_mode) = SET_TIMER_MODE;
}
break;
}
interval:
- interval: 1sec
then:
- if:
condition:
# Turn the iron switch off when the countdown reaches zero
lambda: 'return (id(countdown) == 0) && (id(rotary_encoder_mode) == COUNTDOWN_MODE);'
then:
- logger.log: "Countdown reached zero ==> Turn switch OFF"
- homeassistant.service:
service: switch.turn_off
data:
entity_id: $switch_name
- lambda: |-
id(rotary_encoder_mode) = IDLE_MODE;
id(shutdown_early) = false;
- if:
condition:
# Turn the iron switch off if user turned encoder all the way to zero during countdown
lambda: 'return (id(knob).state == 0.0) && ((id(rotary_encoder_mode) == SET_TIMER_MODE) || (id(rotary_encoder_mode) == COUNTDOWN_MODE));'
then:
- logger.log: "User adjusted encoder to zero ==> Turn switch OFF"
- homeassistant.service:
service: switch.turn_off
data:
entity_id: $switch_name
- lambda: |-
id(rotary_encoder_mode) = IDLE_MODE;
id(countdown) = 0;
id(shutdown_early) = false;
- interval: 30sec
then:
# turn display off if encoder not moved for 30 seconds
- lambda: |-
static int old_encoder = 0;
if ((id(rotary_encoder_mode) != COUNTDOWN_MODE) && (old_encoder == id(knob).state)) {
//ESP_LOGW("interval", "Encoder same for 30 seconds, display OFF");
id(led_display).set_brightness(0.0);
id(rotary_encoder_mode) = IDLE_MODE;
}
else {
//ESP_LOGW("interval", "Encoder different or counting down");
old_encoder = id(knob).state;
}