Introduction

At the time when we were buying a washing machine and a dryer, the “smart” ones were €1500 more, but they didn’t offer higher quality. I also wondered why such a machine would need to be ‘smart’ and what that even meant. Soon after the purchase and a few forgotten rags in the machine, it dawned on me what kind of smart features this kind of machine would need. Although buying a factory “smart” machine would have solved the problem (I imagine and hope you get a notification on your phone when the machine finishes), it was this that forced me to implement a superior solution.

The goal is simple - when the machine finishes, we need to get notified via a Telegram notification. I did this successfully shortly afterwards, but it didn’t help. In the flood of notifications on your phone, you quickly overlook it. I was back to square one when I realised that we needed to resort to more aggressive and persistent methods - an audible notification that would repeat every 10 minutes until someone came downstairs and picked up the rag. This created additional challenges as I had to give the house an understanding of what exactly was happening with the machines.

The end result is rags that don’t smell of mould and are not wrinkled!

Hardware Setup

An easy way to detect what a machine is doing is by measuring its consumption. Every smart plug can do this. I had the additional requirement that it supports the installation of third-party firmware that integrates better into Home Assistant. Most WiFi devices based on the ESP8266 or ESP32 module allow this. The firmware of choice is, as usual, Tasmota.

A specific smart plug that works very well for about 3 years and supports high power is Delock 11827

Integration with Home Assistant

Here’s how the Tasmotized smart plug integrates with Home Assistant using MQTT:

1
2
3
4
5
6
7
mqtt:
  sensor:
    - name: "Washing Machine Power"
      unique_id: "Washing Machine Power"
      state_topic: "tele/basement/utility_room/outlet/washing_machine/SENSOR"
      value_template: '{{ value_json["ENERGY"]["Power"] }}'
      unit_of_measurement: "W"

Automation Logic

  1. The detection of a completed washing machine programme is done via the consumption measurement. If the power of the washing machine is above 500W for 1 minute (means the machine has started working) and the next time it drops below 4W for 30 seconds, this means the programme is finished. Exact values vary from machine to machine, of course. For mine it looks like this:
  1. The message is sent to the Telegram channel.

  2. The text “The washing machine has washed” is turned into Slovenian speech using the Microsoft TTS integration, which connects Home Assistant to the TTS service in Azure Cognitive Services. The speech is then played over the Chromecast soundbar in the central room.

  1. Voice alerts are played every 10 minutes until the dryer is detected to be operating in a similar way to that described in point 1. This means that someone has successfully moved the rags into the dryer.

  2. When the tumble dryer power drops with the end of the programme, the Telegram message is sent again and the text “Dryer has finished” is played.

  3. The voice alerts are repeated again every 10 minutes until the opening of the dryer door is detected by the Mi Window and Door Sensor, which is attached somewhere near the side of the dryer door.

Automation in Home Assistant that implements points 1 and 2:

The final action of the above automation is to change the state of the pralni_stroj_not_emptied variable to indicate that the machine has finished. This change causes the below Appdaemon app to run, which implements points 4 and 6:

 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
import appdaemon.plugins.hass.hassapi as hass
from datetime import datetime, timedelta

PRALNI_STROJ_SOUND = 'Pralni stroj je opral'
SUSILNI_STROJ_SOUND = 'Sušilni stroj je končal'
NOTIFICATION_EVERY_SECONDS = 600

class StrojiNotifications(hass.Hass):
    def initialize(self):
        self.announcements = self.get_app('announcements')
        self.listen_state(self.pralni_stroj_conditions, 'input_boolean.pralni_stroj_not_emptied', new='on', duration=1)
        self.listen_state(self.susilni_stroj_conditions, 'input_boolean.susilni_stroj_not_emptied', new='on', duration=1)
        self.run_daily(self.reset_vars, '03:30:00')

    def pralni_stroj_conditions(self, *args, **kwargs):
        if self.get_state('input_boolean.pralni_stroj_not_emptied') == 'off':
            return
        if int(self.get_state('sensor.moc_susilni_stroj')) > 50:
            self.set_state('input_boolean.pralni_stroj_not_emptied', state='off')
            return
        self.announcements.instant_announcement_message(message=PRALNI_STROJ_SOUND)
        self.run_in(self.pralni_stroj_conditions, NOTIFICATION_EVERY_SECONDS)

    def susilni_stroj_conditions(self, *args, **kwargs):
        if self.get_state('input_boolean.susilni_stroj_not_emptied') == 'off':
            return
        start = datetime.now() - timedelta(seconds=NOTIFICATION_EVERY_SECONDS * 2)
        status_history = self.get_history(entity_id="binary_sensor.ds_susilni_stroj_opening", 
                                        start_time=start)[0]
        if len([evnt for evnt in status_history if evnt.get('state') == 'on']) > 0: # on means opened door sensor
            self.set_state('input_boolean.susilni_stroj_not_emptied', state='off')
            return
        self.announcements.instant_announcement_message(message=SUSILNI_STROJ_SOUND)
        self.run_in(self.susilni_stroj_conditions, NOTIFICATION_EVERY_SECONDS)

    def reset_vars(self, kwargs):
        self.call_service('input_boolean/turn_off', entity_id='input_boolean.pralni_stroj_not_emptied')
        self.call_service('input_boolean/turn_off', entity_id='input_boolean.susilni_stroj_not_emptied')