Add washing machine

This commit is contained in:
Jakob Lechner 2023-12-19 01:45:56 +00:00
parent 28c41482c5
commit 655beac2f7
No known key found for this signature in database
GPG key ID: 996082EFB5906C10
6 changed files with 350 additions and 3 deletions

View file

@ -9,7 +9,7 @@ dkim-keys:
radicale-htpasswd: ENC[AES256_GCM,data:Q0WnleP9I4xozsL/H+5oV3Ag7khfalV40A6ub+DA07U8UKna3/ju533RmjWOnETzSNa6XK140nfCcfGZCiqGyF9tfuuXcKFu+j4=,iv:87PSvHyKF7QUQZmEuxM+IT0VKSGnS0MjoUmCqJ+6tzI=,tag:yrP3TgxE8aSZf0MrCF9dsQ==,type:str]
synapse-turn-shared-secret: ENC[AES256_GCM,data:Q1XRds3Zud1kYkvD6s9WUzP+kNDNsxB5SHd6oCAaLCHhHhYENSAYTZOF+rGjCPNyKFL0e/A=,iv:zScRQrz+pXHNUh/BGOaV+TVnDR3wu1Z/UO1zXarKwtA=,tag:ckpVziE+yb0FjctcT7tAkg==,type:str]
rmfakecloud: ENC[AES256_GCM,data:ktKBKb6cRv1VF8tRvXIpxIy9hPinVPKK05mgvYzz18PEdcrCLpldm5xf7ffHtY5XzDOAMXDCiz6x4xyv7071frrF0spOEPnIzVhxwG8H2Ck=,iv:qJdHjv0RziAs4G9UGeRwGQ4GE5kaObJWpIYWpRKhr9c=,tag:PXgvU1hZK/gvWGyFJaHekg==,type:str]
esphome: ENC[AES256_GCM,data:RykNTaLwXD9pvCepexxPv0TY5Hk1c1JQWerehuaTSfYIPJ8/tXWQ8XJWoHs7+wJZCV+7nXY9ZpWDlWdWUqDfKcHnv62BdwnuW5L9baY1PLq4WISpRKsND99euDLuicvbpej5GBwMhxr7XFKVkAlbEpAEj1c+qAv27gEg0tC4Jkn8nl4+qi+f9TeRXv1miijAFgxqZ+C+9nQ2NM6zeMXgD5NS0PqzgP43NhTrC8s0QYOb9Gry78jEbzEfvquFJ+CmqEwoL3NZ/ynQQlO3IUfOwmJlMyhEVmetPs8sMyMIbmHrLlfCKb9KFUjZCJAsJhSq2QgByPB6mHotd4Nu9P/1xkzuy+hQImrlIK7Sz4bo1tp9CM5czRwU6GOmG7haC2UebSSGzhp8cHnhDtMbKbyqDLdTBsn9ilI/lvzNbuvyvygcaos5a8o3nO9MtVrgvUKowIb5wOHxvUrr8fXIAyrLEAdMVNl0UtPlJSBCRe8WFulhqGrgGECzt/seBoRZvzsgwXnSZgbYGTjIA7UR//0J4p877HFQTdEiUVEudK9vp3u8/GoDKThIy3A=,iv:mVL86y99wQ2M8kum2IotrYQbqswcTgcglCQSegoYzFI=,tag:ZhW1eCd6B59eNhipTb/2Mg==,type:str]
esphome: ENC[AES256_GCM,data:8mXqZnGaiWTO3pNozXCG/NmPEb9fTWBIs/2gX4xLPQwoUg8rw18pVfm0GbEQG1e/crV8ykeYcbKvvZdvVIrc4M/khoO7iuqJHURTcoQh/q/9pH1K2Tg+f7w6V24BozP3GnHXGo+zfo4gX8TgQzx7kktMIiWWnP5gyc1KOw5mQlGmOwfe/Ny6ziCWuGOCPIzd4qtMGOn2lie9HztJ5h0HR9NMJar007grRUS/DXtgw4Z6/PQ80bQJPJF3qsCuJuW9YiI1ImqherYiGaS+ptdZI/nkj9RZFrDBf1NOt1ZizROoeKKO0uJ134Rh+YeuAtWeT+NcQMnj20YdJ75aBHGd+Kx/fICOmO/yXKyuwk1ZyvIvnlauG0bgi2nMrAQJdgO7FRBflmZd6IIFv6a2kOHkS8sigBi2Movq/mZTEF13XBsFxgEZugEqh5MvEd10iaLusYNCa5pc3z5DoeAcbsOGfmcgy4RdHurtVImfLSvPyelRAsWXceOPzU+zLVyAGZ4MeqP5cO2oK1qC9sntMz3s5sjl7BsT9SieiaScK4Tdadh0ZjlzVYzh+XIDTvbgUu6EFmU3t4UeAanPywJIdnnJVMCvgN6FLHk098h9PL2DgKWfS8aiywKXctXUA78WYpYInqS531liF8Jc5HM91Azp1q+ImpzmZjfm0kgD7wLiGzZaPeKsQaB7ykMpvWpTSq+QHW4=,iv:qLn56QxmWFaYtz3MwZyCP7NEfypmWRW4HxQiZmn27fE=,tag:9bHVx5NDf3nZG0alxb4p1w==,type:str]
sops:
kms: []
gcp_kms: []
@ -25,8 +25,8 @@ sops:
TjdZRldhSzVtMkVoTzY1NjdGbCswRVUK0pi+8UuLqRmytcR2ikxOAM02iccl8P1y
ixv0PKPLd+vQ23QeeQy/TfoGx16XttaDUnUrPLZR3TUKtAcld8+m6w==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2023-12-13T21:51:59Z"
mac: ENC[AES256_GCM,data:EIJ8A537IFnubCKL1QCZbhnsPG1qCDt2jGiYCaOolTvxzuB30xxSpqaUE++9wZopQbKn8MAayaO06zaj4D0ix+CrYokPqpI73/XC+rO33YZMQzb72fbRoy9oPQdb3LSlqNyNP7Gu7/Q/ByfEacLlg+wD+e6mmueHmnmF7wt1lZ8=,iv:vhF/KmogG7jJgl1jflvdlRwuA569xL0DEjjozXrqQRI=,tag:NyYvcbqFWwq89IJ0NeF3oA==,type:str]
lastmodified: "2023-12-18T16:45:32Z"
mac: ENC[AES256_GCM,data:ZsC4C+VpUnHuMyqHa3/TIursyWDPV1+we/cBrUhyeqeNdeDveJ4a26jmIzNxWWHbJU7uMPy6BF6iHpS91YK4m+KKSqoXZiTjB8b9p3y9ietT9XQOLXdt8pxVFBEOSH7g9DeVbrJUHTOhzYpugMqC/QXcDLE1GQwa0eGUq3JIDh8=,iv:vbDpVJAXvYw1qQ5Or6CxOs8OO4yhtlVF4zh07bkkFJw=,tag:IqVAItuv23AbbGQsPh5dsg==,type:str]
pgp:
- created_at: "2023-05-02T19:30:42Z"
enc: |

View file

@ -0,0 +1 @@
CODEOWNERS = ["@jalr"]

View file

@ -0,0 +1,155 @@
#include "miele_w433.h"
#include "esphome/core/log.h"
#include <string>
namespace esphome {
namespace miele_w433 {
static const char *const TAG = "miele_w433";
static const std::string OPERATION_STATES[] = {"inaktiv", "Einw./Vorwäsche", "Waschen", "Spülen", "Spülstop", "Pumpen", "Endschleudern", "Knitterschutz/Ende"};
void MieleW433Sensor::setup() {
this->pin_clock_->setup();
this->store_.pin_clock = this->pin_clock_->to_isr();
this->pin_clock_->attach_interrupt(MieleW433SensorStore::gpio_intr_clock, &this->store_, gpio::INTERRUPT_RISING_EDGE);
this->pin_data_->setup();
this->store_.pin_data = this->pin_data_;
if (this->pin_en_lamps_ != nullptr) {
this->pin_en_lamps_->setup();
this->store_.pin_en_lamps = this->pin_en_lamps_->to_isr();
this->pin_en_lamps_->attach_interrupt(MieleW433SensorStore::gpio_intr_en_lamps, &this->store_, gpio::INTERRUPT_RISING_EDGE);
}
if (this->pin_en_7seg_ != nullptr) {
this->pin_en_7seg_->setup();
this->store_.pin_en_7seg = this->pin_en_7seg_->to_isr();
this->pin_en_7seg_->attach_interrupt(MieleW433SensorStore::gpio_intr_en_7seg, &this->store_, gpio::INTERRUPT_RISING_EDGE);
}
}
void MieleW433Sensor::loop() {
if (millis() - this->last_update_ > 1000) {
uint32_t dpy_7seg;
uint32_t dpy_lamps;
{
InterruptLock lock;
dpy_7seg = this->store_.dpy_7seg;
dpy_lamps = this->store_.dpy_lamps;
}
uint8_t minutes = (dpy_7seg & 0xf) * 60;
minutes += ((dpy_7seg>>8) &0xf);
minutes += ((dpy_7seg>>4) & 0xf) * 10;
if (this->time_remaining_sensor_ != nullptr) {
this->time_remaining_sensor_->publish_state(minutes);
}
uint8_t state = 0;
bool is_active = false;
switch(dpy_lamps & 0x1f007) {
case 1<<0:
// Pumpen
state = 5;
is_active = true;
break;
case 1<<1:
// Endschleudern
state = 6;
is_active = true;
break;
case 1<<2:
// Knitterschutz/Ende
state = 7;
break;
case 1<<12:
// Einw./Vorwäsche
state = 1;
is_active = true;
break;
case 1<<13:
// Waschen
state = 2;
is_active = true;
break;
case 1<<14:
// Spülen
state = 3;
is_active = true;
break;
case 1<<15:
// Spülstop
state = 4;
is_active = true;
break;
}
if (this->current_operation_sensor_ != nullptr) {
this->current_operation_sensor_->publish_state(OPERATION_STATES[state]);
}
if (this->active_sensor_ != nullptr) {
this->active_sensor_->publish_state(is_active);
}
this->last_update_ = millis();
}
}
void MieleW433Sensor::dump_config() {
LOG_SENSOR("", "Miele W433", this);
LOG_PIN(" Pin Clock: ", this->pin_clock_);
LOG_PIN(" Pin Data: ", this->pin_data_);
LOG_PIN(" Pin Enable 7-Segment: ", this->pin_en_7seg_);
LOG_PIN(" Pin Enable Lamps: ", this->pin_en_lamps_);
};
void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_clock(MieleW433SensorStore *arg) {
if (!arg->pin_en_7seg.digital_read()) {
if (arg->count_cycles_7seg <= 31) {
if (arg->pin_data->digital_read()) {
arg->tmp_7seg |= (1<<(31-arg->count_cycles_7seg));
}
else {
arg->tmp_7seg &= ~(1<<(31-arg->count_cycles_7seg));
}
arg->count_cycles_7seg++;
}
}
if (!arg->pin_en_lamps.digital_read()) {
if (arg->count_cycles_lamps <= 31) {
if (arg->pin_data->digital_read()) {
arg->tmp_lamps |= (1<<(31-arg->count_cycles_lamps));
}
else {
arg->tmp_lamps &= ~(1<<(31-arg->count_cycles_lamps));
}
arg->count_cycles_lamps++;
}
}
}
void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_en_lamps(MieleW433SensorStore *arg) {
if (arg->count_cycles_lamps == 8) {
arg->ctrl_lamps = arg->tmp_lamps>>24;
}
else if (arg->count_cycles_lamps == 24) {
arg->dpy_lamps = arg->tmp_lamps>>8;
}
arg->count_cycles_lamps = 0;
}
void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_en_7seg(MieleW433SensorStore *arg) {
if (arg->count_cycles_7seg == 8) {
arg->ctrl_7seg = arg->tmp_7seg>>24;
}
else if (arg->count_cycles_7seg == 24) {
arg->dpy_7seg = arg->tmp_7seg>>8;
}
arg->count_cycles_7seg = 0;
}
} // namespace miele_w433
} // namespace esphome

View file

@ -0,0 +1,62 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/automation.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
namespace miele_w433 {
struct MieleW433SensorStore {
ISRInternalGPIOPin pin_clock;
InternalGPIOPin *pin_data;
ISRInternalGPIOPin pin_en_7seg;
ISRInternalGPIOPin pin_en_lamps;
static void gpio_intr_clock(MieleW433SensorStore *arg);
static void gpio_intr_en_7seg(MieleW433SensorStore *arg);
static void gpio_intr_en_lamps(MieleW433SensorStore *arg);
volatile int count_cycles_lamps = 0;
volatile int count_cycles_7seg = 0;
volatile uint32_t tmp_lamps;
volatile uint32_t tmp_7seg;
volatile uint32_t dpy_lamps;
volatile uint8_t ctrl_lamps;
volatile uint32_t dpy_7seg;
volatile uint8_t ctrl_7seg;
};
class MieleW433Sensor : public sensor::Sensor, public Component {
public:
void set_pin_clock(InternalGPIOPin *pin_clock) { pin_clock_ = pin_clock; }
void set_pin_data(InternalGPIOPin *pin_data) { pin_data_ = pin_data; }
void set_pin_en_7seg(InternalGPIOPin *pin_en_7seg) { pin_en_7seg_ = pin_en_7seg; }
void set_pin_en_lamps(InternalGPIOPin *pin_en_lamps) { pin_en_lamps_ = pin_en_lamps; }
void setup() override;
void loop() override;
void dump_config() override;
void set_time_remaining_sensor(sensor::Sensor *sensor) { this->time_remaining_sensor_ = sensor; }
void set_active_sensor(binary_sensor::BinarySensor *sensor) { this->active_sensor_ = sensor; }
void set_current_operation_sensor(text_sensor::TextSensor *sensor) { this->current_operation_sensor_ = sensor; }
protected:
InternalGPIOPin *pin_clock_;
InternalGPIOPin *pin_data_;
InternalGPIOPin *pin_en_7seg_{nullptr};
InternalGPIOPin *pin_en_lamps_{nullptr};
MieleW433SensorStore store_{};
uint32_t last_update_ = 0;
sensor::Sensor *time_remaining_sensor_{nullptr};
binary_sensor::BinarySensor *active_sensor_{nullptr};
text_sensor::TextSensor *current_operation_sensor_{nullptr};
//enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE};
};
} // namespace miele_w433
} // namespace esphome

View file

@ -0,0 +1,88 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import binary_sensor, sensor, text_sensor
from esphome.const import (
CONF_ACTIVE,
CONF_BEEPER,
CONF_CLOCK_PIN,
CONF_CURRENT_OPERATION,
CONF_DATA_PIN,
CONF_DURATION,
CONF_ID,
CONF_STATUS,
DEVICE_CLASS_LOCK,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SWITCH,
ICON_ROTATE_RIGHT,
ICON_TIMELAPSE,
UNIT_MINUTE,
)
CONF_EN_7SEG_PIN = "enable_7segment_pin"
CONF_EN_LAMPS_PIN = "enable_lamps_pin"
CONF_TIME_REMAINING = "time_remaining"
CONF_WATER_INLET = "water_inlet"
CONF_WATER_OUTLET = "water_outlet"
ICON_PLAY = "mdi:play"
ICON_PLAYLIST_PLAY = "mdi:playlist-play"
ICON_WATER_OFF = "mdi:water-off"
AUTO_LOAD = ["text_sensor", "binary_sensor"]
DEPENDENCIES = []
miele_w433_ns = cg.esphome_ns.namespace("miele_w433")
MieleW433Sensor = miele_w433_ns.class_("MieleW433Sensor", sensor.Sensor, cg.Component)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(MieleW433Sensor),
cv.Required(CONF_CLOCK_PIN): cv.All(pins.internal_gpio_input_pin_schema),
cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema),
cv.Optional(CONF_EN_7SEG_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_EN_LAMPS_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_TIME_REMAINING): sensor.sensor_schema(
unit_of_measurement=UNIT_MINUTE,
icon=ICON_TIMELAPSE,
accuracy_decimals=0,
),
cv.Optional(CONF_ACTIVE): binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_RUNNING, icon=ICON_PLAY
),
cv.Optional(CONF_CURRENT_OPERATION): text_sensor.text_sensor_schema(
icon=ICON_PLAYLIST_PLAY
),
# cv.Optional(CONF_WATER_INLET): binary_sensor.binary_sensor_schema(
# device_class=DEVICE_CLASS_PROBLEM,
# icon=ICON_WATER_OFF
# ),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(
config[CONF_ID],
)
await cg.register_component(var, config)
pin_clock = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN])
cg.add(var.set_pin_clock(pin_clock))
pin_data = await cg.gpio_pin_expression(config[CONF_DATA_PIN])
cg.add(var.set_pin_data(pin_data))
if CONF_EN_7SEG_PIN in config:
pin_en_7seg = await cg.gpio_pin_expression(config[CONF_EN_7SEG_PIN])
cg.add(var.set_pin_en_7seg(pin_en_7seg))
if CONF_TIME_REMAINING in config:
sens = await sensor.new_sensor(config[CONF_TIME_REMAINING])
cg.add(var.set_time_remaining_sensor(sens))
if CONF_EN_LAMPS_PIN in config:
pin_en_lamps = await cg.gpio_pin_expression(config[CONF_EN_LAMPS_PIN])
cg.add(var.set_pin_en_lamps(pin_en_lamps))
if CONF_ACTIVE in config:
sens = await binary_sensor.new_binary_sensor(config[CONF_ACTIVE])
cg.add(var.set_active_sensor(sens))
if CONF_CURRENT_OPERATION in config:
sens = await text_sensor.new_text_sensor(config[CONF_CURRENT_OPERATION])
cg.add(var.set_current_operation_sensor(sens))

View file

@ -0,0 +1,41 @@
esphome:
name: "waschmaschine"
friendly_name: "Waschmaschine"
api:
encryption:
key: !secret apikey_waschmaschine
ota:
password: !secret otapass_waschmaschine
wifi:
ssid: !secret wifi_ssid_bw
password: !secret wifi_password_bw
domain: .iot.bw.jalr.de
esp32:
board: esp32doit-devkit-v1
framework:
type: arduino
version: recommended
logger:
external_components:
- source: components
components: [ miele_w433 ]
sensor:
- platform: miele_w433
enable_7segment_pin: 27
clock_pin: 14
enable_lamps_pin: 26
data_pin: 13
time_remaining:
name: "verbleibende Zeit"
active:
name: "in Betrieb"
current_operation:
name: "aktueller Vorgang"