Blackjack y protección contra fugas

Saludos Existe tal cosa - hydrolock \ neptune \ avkvastorozh - sistemas de cierre de agua si se produce una fuga incontrolada. El principio es simple: un sensor de agua + automatización + un par de grifos eléctricos. Pero el diablo, como de costumbre, está en los detalles: cómo están dispuestos los grifos, cómo están dispuestos los sensores de fugas y por qué uno cuesta 50 rublos y el otro cuesta 500 rublos. Un kilogramo de un boletín de diseño se envolverá alrededor de todo esto, el empaque te sacará los ojos, etc.

En la historia, caminaré a través de los ladrillos del sistema, que me guió en la elección. Todo el sistema está construido con sensores de fábrica y un controlador casero basado en Particle (por ejemplo, Park) Photon (como un esp8266 que tiene un IDE de nube en el cableado de la caja), la base del dispositivo es controlador stm + módulo wifi de Broadcom. Todo esto está vinculado a un servidor openhab en Orange Pi One.



¿Por qué no un sistema terminado?

- Porque puedo hacerlo yo mismo y es alto
- La integración con sistemas externos es escasa en los sistemas listos para usar.
- Los sistemas listos para usar no tienen funciones auxiliares: tener en cuenta las lecturas del medidor, los sensores de temperatura del agua, la notificación de cortes de agua y otras fantasías eróticas para caminar.

Comencemos con grúas


Elija estúpidamente en la frente en términos de torque. Durante algún tiempo vivió en los suburbios, donde la calidad del agua (como probablemente en todas partes en Zamkadye) deja mucho que desear. Entonces, las válvulas de bola de 1/2 pulgada si no toca el año, es muy difícil girar. Y en el toallero térmico de 1 pulgada, ni siquiera trato de moverlo, solo si fortalezco el hombro con una llave, pero aquí puedes arrancar algo. El problema está en los depósitos de calcio-madera dura, "cubiertos de vegetación" con una sola palabra.

En consecuencia, la elección recayó en la serie profesional del hidrolock: 21 N * m de torque no parece una charla publicitaria, la grúa es simplemente enorme: evalúe el lugar de su instalación antes de la compra.



El grifo está sellado, un sello de goma alrededor del perímetro, la entrada debajo del prensaestopas.
Retirar la tapa.



Ante nosotros está la parte superior del tablero y el motor paso a paso. Todo esto funciona con 12 voltios. Cortar el cable de control a tierra pone la válvula en la posición cerrada. En el tablero vemos un simple controlador PIC 12f629. Vivió, el controlador en la unidad de la grúa.

La parte posterior del tablero es la más interesante.



L293 shagovik driver y photocoupler (emisor + fotodetector). Ella mira el engranaje principal de la unidad, que está pintado en partes: blanco y negro, cerrado / abierto.



La grúa gira todo el tiempo en una dirección, la lógica del controlador es simple: giramos el eje hasta que cambiemos al color deseado. La rotación de la grúa en una dirección es menos desgaste, y es menos probable que la forma sin contacto de determinar la posición provoque un mal funcionamiento / mal funcionamiento de una resistencia variable o un interruptor de límite.

Para la instalación, puede desenroscar la grúa de la unidad; se sujeta con 2 tuercas. Hay una junta aislante de calor entre el variador y la grúa.



Tuve la reparación hace un año y medio. La grúa se compró hace tres años: para desmontar, mirar dentro, comprar más y enrollarla durante la reparación. Sí, ahora ... lo máximo que logré en este circo infernal fue colocar un recolector de suciedad en el ensamblaje del suministro de agua con la posibilidad de reemplazarlo con una grúa.
Y solo después de año y medio, compré una segunda grúa y la atornillé.

Como resultado, observamos un fenómeno extraño y raro (leído en la voz de Drozdov): se confirmó toda la información del sitio web del fabricante. Además, la descripción es peculiar, como si los técnicos escribieran, y luego el marketing fue regado para la gente, pero todavía pocas personas entienden todos los chips. No hay suficiente sección en el sitio, para integradores con detalles técnicos en su interior. Incluso a expensas del aumento del torque, no se encontraban al inicio: la grúa al comienzo golpea con un motor de 1.5A y después de 2-3 segundos comienza a subir en modo normal (corriente 0.7 A). Tarda unos 25-30 segundos en cerrarse.

Otra experiencia: a expensas del par, es excesivo para el tiempo de Moscú, aquí el agua está bastante bien, durante un año y medio en un filtro de 100 μm hay un par de escoria y no se produce un crecimiento excesivo. Tienes que pagar por el gran torque con el precio, el tiempo de apertura y el lugar en el armario. Creo que habrá suficientes unidades convencionales de Hydrolock Ultimate, Neptune o Aquastorozh. No puedo responder por los dos últimos: no lo logré, hace unos 5 años tenían engranajes parcialmente de plástico, ahora parecen haberlo arreglado.

También hay un Winrolock Winner con conexión directa de sensores al variador, esto es si no necesita todo lo que he hecho. Allí, la potencia es autónoma de 4 baterías, y la base es similar a una unidad definitiva. En general, también es potencialmente interesante para un controlador hecho a sí mismo: tiene 5 voltios, no necesita poner dos buses en 5 y 12 voltios y puede tirar el aislamiento óptico.

Sensores de fugas


Compré los sensores del mismo contador WSU - universal. Tienen dos salidas de "colector abierto", una tira al suelo solo si hay agua, la segunda, si entra agua, tira al suelo todo el tiempo hasta que se corta la energía. Solo uso la primera salida, el resto de la lógica está en el controlador, pero parece que esta salida puede ser útil para algunos sistemas de despacho de condominios.

Los cables en el kit son tres metros en alguna parte. El color de los cables es Ad_i_Israel. Mira la cita:

cable rojo (marrón) (Vcc) alimentado de +5 a +30 voltios.
cable negro (blanco) (OUT2)
cable verde (OUT1)
cable amarillo (GND)

Esto es lo que evitó hacer tierra blanca / negra? En el accionamiento de la grúa, por cierto, los cables en color con lógica no son ale. El primer sensor está en la cocina, debajo del fregadero al lado del lavavajillas.



El segundo en el baño en una zanja de drenaje especial. Cuando hizo la regla, no la trajo a la pared. El resultado fue una especie de sumidero para recoger agua del baño y del inodoro.



Según la experiencia operativa, ya había una falsa alarma del sensor en el lavavajillas. A juzgar por el registro de un ciclo de sondeo (500 ms) hubo un circuito, modificó el código: el cambio de estado ahora se produce a 10 valores idénticos consecutivos del sensor.

Los contactos del sensor están chapados en oro. Un amigo ha tenido sensores similares durante varios años, no se ha notado oxidación.

Sensores de presión


Prácticamente - showometers. Precisión + - 0.5 atm me convenía completamente. Según los sensores, aparece una alerta de cierre de agua. Compré en Ali aquí .

Sensores de temperatura


¿Y por qué no agregar? De utilidad: podrá informar una vez al año sobre el cierre del agua caliente. Banal ds18b20 usado.

Contadores


El Itelma más común, una vez cada 10 litros hace contacto. En el lado del controlador, la salida se eleva a + 3.3v, el medidor la tira al suelo.

Controlador




Dentro



Basado en Particle Photon, más detalles aquí . Tienen una versión con un módulo 2G o 3G (Electron). El primer firmware fue escoria completa, parpadeando con diodos OK, pero tan pronto como comience la salchicha masticable complicada, jugar con i2c e interrupciones puede perder wifi. Ahora puedes vivir. En principio, puede arrojar sensores de presión fuera del circuito y agitar todo en el ESP8266; adelante. Primero, el fotón debe estar vinculado a la cuenta de partículas (a través de la aplicación en el móvil o a través de la consola Particle CLI; solo uso el segundo método) y registrar una red wifi. Después del enlace, aquí en la sección del dispositivo aparece el controlador y su estado de conexión a la nube.



Tengo todos los nodos conectados a la nube solo para actualizar el firmware. No es que sea paranoico, solo trabajar con la nube no consume recursos de controlador ricos. El IDE admite trabajar con bibliotecas, literalmente una docena cuenta con el respaldo del propio contador, el resto con la comunidad. En mi observación, todo lo común ha sido portado durante mucho tiempo, y otra característica es que el IDE sabe inmediatamente cuántos proyectos usan la biblioteca.

Código fuente del firmware
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_SSD1306/Adafruit_SSD1306.h"

// This #include statement was automatically added by the Particle IDE.
#include "MQTT/MQTT.h"

// This #include statement was automatically added by the Particle IDE.
#include "OneWire/OneWire.h"

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
STARTUP(WiFi.selectAntenna(ANT_EXTERNAL));
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

struct counter_struct {
  float value;
  byte state;
  int pin;
};
struct valve_struct {
  byte state;
  int pin;
};
struct sensor_struct {
  int timeout;
  byte state;
  int pin;
};

unsigned long currentMillis = 0;
unsigned long previous_conected = 100000; //  
unsigned long previous_wifi_uptime = 100000; //  
unsigned long previous_counter_read = 0; //  
unsigned long wifi_uptime;
unsigned long start_temp_timer = 0;
unsigned long read_temp_timer = 0;
byte display_timeout = 0;

//temp onewire 
OneWire ds0 = OneWire(D2);
OneWire ds1 = OneWire(D3);
byte addr0[8];
byte addr1[8];
bool presense0 = false;
bool presense1 = false;
byte data[12];

#define OLED_RESET A7
Adafruit_SSD1306 display(OLED_RESET);


//valve control
retained valve_struct valve[2] = { {0, D4}, {0, D5} };

//counter control
retained counter_struct counter[2] = { {0, 1, A0}, {0, 1, A1} };
volatile int pressure[2] = {A2, A3};
#define SENSOR_TIMEOUT 10
volatile sensor_struct sensor[2] = { {0, 1, D6}, {0, 1, D7} };

void callback(char* topic, byte* payload, unsigned int length);

byte server[] = { 192,168,2,101};
MQTT client(server, 1883, callback);

bool publish_message(const char* t, const char* p, bool retain) 
{
    return client.publish(t, (uint8_t*)p, sizeof(p), retain);
}

bool publish_message(const char* t, int p, bool retain) 
{   
    char buf_d[12];
    int n = sprintf(buf_d,"%d",p);
    return client.publish(t, (uint8_t*)buf_d, n, retain);
}

bool publish_message(const char* t, float p, bool retain) 
{   
    //char buf_f[18];
    String s(p, 4);
//    dtostrf(p, 9, 4, buf_f);
    //int n = sprintf(buf_f,"%f",p);
    return client.publish(t, (uint8_t*)s.c_str(), s.length(), retain);
}

// recieve message
void callback(char* topic, byte* payload, unsigned int length) {
    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;
    String message(p);
    String t(topic);
    if (t.equals("home/water_count/spark/set"))
    {
        if (message.equalsIgnoreCase("1"))
        {
            Particle.connect();
            if (waitFor(Particle.connected, 10000)) 
                {publish_message("home/water_count/spark", 1, false);}
            else
                {Particle.disconnect(); publish_message("home/water_count/spark", 0, false);}
        }
        else
        {
            Particle.disconnect();
            publish_message("home/water_count/spark", 0, false);
        }
    }
    else if (t.startsWith("home/water_count/valve/"))
    {
        int m = message.toInt();
        int x = t.substring(23,24).toInt();
        if (m > -1 && m < 2 && x > -1 && x <2) 
        {
            set_valve(x, m);
        }
        else
        {
            publish_message("home/water_count/valve/" + t.substring(23,24),  valve[x].state , true);
        }
    }
    else if (t.startsWith("home/water_count/counter/"))
    {
        float m = message.toFloat();
        int x = t.substring(25,26).toInt();
        if (m > -1 && m <= 999999 && x > -1 && x <2) 
        {
            counter[x].value = m;
        }
        publish_message("home/water_count/counter/" + t.substring(25,26),  counter[x].value , true);
    }
}

void setup() {
//Serial.begin(9600);
    WiFi.on();
    WiFi.connect();
    if (waitFor(WiFi.ready, 5000)) {mqtt_connect();}
    for (int i=0; i < 2; i++) 
    {
        pinMode(valve[i].pin, OUTPUT);
        digitalWrite(valve[i].pin, valve[i].state);
        pinMode(counter[i].pin, INPUT);
        pinMode(sensor[i].pin, INPUT);
        counter[i].state = digitalRead(counter[i].pin);
        pinMode(pressure[i], AN_INPUT);
    }
    pinMode(A4, INPUT_PULLUP);

    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
    display.clearDisplay();   // clears the screen and buffer

    //Particle.connect();
}

void loop() 
{
    currentMillis = millis();
    //       MQTT 
    if (currentMillis - previous_conected >= 30000 || previous_conected > currentMillis)
    {
        previous_conected = currentMillis;
        if (!client.isConnected() & wifi_uptime > 60)
        {
            mqtt_connect();
        }
        publish_message("home/water_count/rssi", WiFi.RSSI(), true);
    }
    if (currentMillis - previous_wifi_uptime >= 1000 || previous_wifi_uptime > currentMillis)
    {
        previous_wifi_uptime = currentMillis;
        WiFi.ready() ? wifi_uptime++ : wifi_uptime = 0;
        //work with button and display
        int fg = digitalRead(A4);
        if (display_timeout > 0)
        {
            display_timeout -= 1;
            if (display_timeout == 0) 
            { 
                display.clearDisplay();
                display.display();
            } 
        }
        if (fg == 0)
        {
            if (display_timeout == 0)
            {
                display.clearDisplay();   // clears the screen and buffer
                display.setTextSize(2);
                display.setTextColor(WHITE);
                display.setCursor(0,0);
                display.print("C=");
                display.println(counter[0].value, 4);
                display.setCursor(0,16);
                display.print("H=");
                display.println(counter[1].value, 4);
                display.setCursor(0,32);
                display.print("Valve=");
                display.print(valve[0].state);
                display.print("|");
                display.println(valve[1].state);
                display.setCursor(0,48);
                display.print("Sensor=");
                display.print(sensor[0].state);
                display.print("|");
                display.println(sensor[1].state);
                display.display();
            }
            display_timeout = 10;
        }
    }
    //counter check
    if (currentMillis - previous_counter_read >= 500 || previous_counter_read > currentMillis)
    {
        previous_counter_read = currentMillis;
        for (int i=0; i < 2; i++) 
        {
            byte count_state = digitalRead(counter[i].pin);
            if (count_state != counter[i].state)
            {
                counter[i].state = count_state;
                if (count_state == 0)
                {
                    counter[i].value += 0.01;
                    char buf18[30];
                    sprintf(buf18,"home/water_count/counter/%d", i);
                    publish_message(buf18 , counter[i].value, true);
                }
            }
            //    
            byte sensor_state = digitalRead(sensor[i].pin);
            if (sensor_state != sensor[i].state) //
            {
                sensor[i].state = sensor_state;
                sensor[i].timeout = SENSOR_TIMEOUT; 
            }
            if (sensor[i].timeout > 0) 
            {
                sensor[i].timeout -= 1;
                if (sensor[i].timeout == 0)
                {
                    char buf18[30];
                    sprintf(buf18,"home/water_count/sensor/%d", i);
                    publish_message(buf18 , sensor[i].state, true);
                    if (sensor[i].state  == 0)
                    {
                        set_valve(0, 1); //close both valve
                        set_valve(1, 1); //close both valve
                    }
                }
            }
        }
    }

    // temp onewire
    if (currentMillis - start_temp_timer >= 299000 || start_temp_timer > currentMillis)
    { // 
        start_temp_timer = currentMillis;
        presense0 = start_temp0();
        presense1 = start_temp1();
    }
    if (currentMillis - read_temp_timer >= 300000 || read_temp_timer > currentMillis)
    {// 
        read_temp_timer = currentMillis;
        start_temp_timer = currentMillis;
        if (presense0) read_temp0();
        if (presense1) read_temp1();
        //preasure calc and send
        char buf18[30]; 
        for (int i=0; i < 2; i++)
        {
            sprintf(buf18,"home/water_count/pressure/%d", i);
            float read_val = analogRead(pressure[i]);
            float value = (read_val - 600.0) / 300.0 ;
            publish_message(buf18 , value, false);
        }
    }
    //Particle.process();
    client.loop();
}

void mqtt_connect()
{
    if (client.connect("water_count"))
    { //  spark    
        client.subscribe("home/water_count/spark/set");
        publish_message("home/water_count/spark", Particle.connected() ? 1 : 0, true);
        client.subscribe("home/water_count/valve/+/set");
        client.subscribe("home/water_count/counter/+/set");
        
    }
}

bool start_temp0()
{
    
    if ( !ds0.search(addr0)) { ds0.reset_search(); return false;}
    ds0.reset_search();
    if (OneWire::crc8(addr0, 7) != addr0[7]) { return false;}
    
    ds0.reset();
    ds0.select(addr0);
    ds0.write(0x44, 0);
    return true; 
}
bool start_temp1()
{
    
    if ( !ds1.search(addr1)) { ds1.reset_search(); return false;}
    ds1.reset_search();
    if (OneWire::crc8(addr1, 7) != addr1[7]) { return false;}
    
    ds1.reset();
    ds1.select(addr1);
    ds1.write(0x44, 0);
    return true; 
}

bool read_temp0()
{
    //delay(1000);
    ds0.reset();
    ds0.select(addr0);
    ds0.write(0xBE, 0);

    for (int i = 0; i < 9; i++) 
    {
        data[i] = ds0.read();
    }
    int16_t raw = (data[1] << 8) | data[0];
    float celsius = (float)raw * 0.0625;
    if (celsius < 0 || celsius > 100) return false;
    publish_message("home/water_count/temp/0", celsius, false);
    //Serial.println(celsius);
    ds0.reset_search();
    return true;
}
bool read_temp1()
{
    //delay(1000);
    ds1.reset();
    ds1.select(addr1);
    ds1.write(0xBE, 0);

    for (int i = 0; i < 9; i++) 
    {
        data[i] = ds1.read();
    }
    int16_t raw = (data[1] << 8) | data[0];
    float celsius = (float)raw * 0.0625;
    if (celsius < 0 || celsius > 100) return false;
    publish_message("home/water_count/temp/1", celsius, false);
    //Serial.println(celsius);
    ds1.reset_search();
    return true;
}

void set_valve(int vlv, byte state)
{
    valve[vlv].state = state;
    digitalWrite(valve[vlv].pin, state);
    char buf26[26];
    sprintf(buf26,"home/water_count/valve/%d", vlv);
    publish_message(buf26 , state , true);
}


A través de MQTT nos conectamos con el corredor. Monitoree los sensores y el casco en las ramas correspondientes de eventos y valores mqtt. Por ejemplo, home / water_count / valve / 0: una unidad de agua. home / water_count / counter / 0: lecturas del contador de agua fría.

Nos suscribimos a los comandos para cambiar el estado de la unidad y configurar el valor actual del contador (agua fría y caliente):

client.subscribe("home/water_count/valve/+/set");
client.subscribe("home/water_count/counter/+/set");

Hay un botón en el dispositivo: al presionar, activamos la pantalla y dibujamos las lecturas actuales de contadores, sensores y toques. Pantalla OLED, se desvanece rápidamente si está encendida todo el tiempo.

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

Esta es una característica interesante de hardware y software del controlador stm, en la Partícula de referencia lo llaman BackupSRAM. El fotón tiene una salida vbat; esto no es energía de la batería ni carga. Mientras haya voltaje en este tramo, el contenido de 4k bytes de SRAM se mantiene con el controlador completamente desenergizado. Esto elimina el problema del desgaste de la EEPROM.

En el código, las variables que deben introducirse en esta memoria se declaran con la indicación: retenidas. Implementé hardware del supercondensador a 1.5F. De acuerdo con la hoja de datos, la memoria morirá a 1.6v, de acuerdo con mis experimentos de banco en el protoboard, vendrá en 2 semanas con aproximadamente mi condensador. La lógica de cerrar las grúas cuando se activan los sensores es "autónoma" y no depende de la conexión a openhab. Hay un interruptor de 3 vías para el control de accionamiento directo: automático, apagado (grifos abiertos), cierre (cierre).

El diagrama de circuito a continuación:



el proyecto Eagle, junto con las bibliotecas personalizadas, se puede descargar aquí .

La tarifa se hizo LUT, en las pistas no se redujo.

Baño de agua El mejor amigo de LUT


Unidad de fuente de alimentación. Necesitamos 12 y 5 voltios. Se busca en el donante en eBay la línea: "adaptador de alimentación del disco duro 5v 12v", como este .

Vivienda


Fue impreso con plástico PLA en una impresora 3D (Tarántula Tevo). Boquilla 0.4mm, capa 0.25mm. La cubierta es al mismo tiempo y la base para montar la placa del controlador. La base con la fuente de alimentación está unida a la pared. La base con la tapa no está sujeta con tornillos, hay suficiente tensión de la tapa (como la tapa de la abuela en frascos de mermelada) y la estructura en capas de las paredes funciona.

Modelo 3D en el archivo .





Así es como se ve todo montado en una tubería de agua.



Openhab


Desplegado en Orange Pi One bajo Armbian.

Configuración del artículo
  -    ,  .
Number	watercount_temp1	"T cool [%.1f °C]"			(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/temp1:state:default]" }
Number	watercount_temp2	"T hot [%.1f °C]"			(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/temp2:state:default]" }
Number	watercount_count0	"Count cool [%.2f ³]"					(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/counter/0:state:default]" }
Number	watercount_count1	"Count hot [%.2f ³]"				(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/counter/1:state:default]" }
Number	watercount_pressure0	"P cool [%.2f .]"			(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/pressure/0:state:default]" }
Number	watercount_pressure1	"P hot [%.2f .]"				(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/pressure/1:state:default]" }
Number	watercount_sensor0	"Sensor0 is [MAP(water_sensor.map):%s]"		(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/sensor/0:state:default]" }
Number	watercount_sensor1	"Sensor1 is [MAP(water_sensor.map):%s]"		(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/sensor/1:state:default]" }
Number	watercount_valve0	"Valve cool"		(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/valve/0:state:default], >[mqtt_bro:home/water_count/valve/0/set:command:*:default]" }
Number	watercount_valve1	"Valve hot"				(gWaterCount)		{ mqtt="<[mqtt_bro:home/water_count/valve/1:state:default], >[mqtt_bro:home/water_count/valve/1/set:command:*:default]" }
String	watercount_sendStr "LastVol:[%s]"						(gWaterCount)
Number	watercount_sendCool "Send cool [%.2f ³]"		(gWaterCount)
Number	watercount_sendHot "Send hot [%.2f ³]"			(gWaterCount)
Number	watercount_sendSwitch "Autosend" 	(gWaterCount)
Number	watercount_rssi	"WaterCount [%d dB]"	(gSpark_RSSI)		{ mqtt="<[mqtt_bro:home/water_count/rssi:state:default]" }
Number	watercount_spark_state	"WaterCount Spark"		(gSpark)		{ mqtt="<[mqtt_bro:home/water_count/spark:state:default], >[mqtt_bro:home/water_count/spark/set:command:*:default]" }


Se necesita una pequeña regla de transformación para el sensor de fugas.

Las configuraciones se transforman
transform\water_sensor.map
1=dry
0=wet
undefined=undefined

Formamos la página para la gestión:

Configs Sitemap
Sitemap
Text label=«» icon=«water»
{
Frame
{
Text item=watercount_temp1
Text item=watercount_count0
Text item=watercount_pressure0
Switch item=watercount_valve0 mappings=[1=«Close», 0=«Open»]
}
Frame
{
Text item=watercount_temp2
Text item=watercount_count1
Text item=watercount_pressure1
Switch item=watercount_valve1 mappings=[1=«Close», 0=«Open»]
}
Frame
{
Text item=watercount_sensor0
Text item=watercount_sensor1
}
Frame
{
Switch item=watercount_sendSwitch mappings=[0=«OFF», 1=«ON»]
Text item=watercount_sendStr
Text item=watercount_sendCool
Text item=watercount_sendHot
}
}

Y reglas de procesamiento:

Reglas de configuración
rule "Check watercount_sensor0"
	when
		Item watercount_sensor0 received update
	then
		if ((watercount_sensor0.state as DecimalType) == 1)
		{
			if ((watercount_sensor0.historicState(now.minusSeconds(3)).state as DecimalType) == 1)
			{
				sendTelegram("****_bot", "Sensor0 was wet less than 5 seconds")
			}
			else
			{
				sendTelegram("****_bot", "Sensor0 become dry")
			}
		}
		else
		{
			if ((watercount_sensor0.historicState(now.minusSeconds(3)).state as DecimalType) == 0)
			{
				sendTelegram("****_bot", "Sensor0 was dry less than 5 seconds");			
			}
			else
			{
				sendTelegram("****_bot", "Sensor0 become wet! Valves will be closed!")
			}
		}
	end
rule "Check watercount_sensor1"
	when
		Item watercount_sensor1 received update
	then
		if ((watercount_sensor1.state as DecimalType) == 1)
		{
			if ((watercount_sensor1.historicState(now.minusSeconds(3)).state as DecimalType) == 1)
			{
				sendTelegram("****_bot", "Sensor1 was wet less than 5 seconds")
			}
			else
			{
				sendTelegram("****_bot", "Sensor1 become dry")
			}
		}
		else
		{
			if ((watercount_sensor1.historicState(now.minusSeconds(3)).state as DecimalType) == 0)
			{
				sendTelegram("****_bot", "Sensor1 was dry less than 5 seconds");			
			}
			else
			{
				sendTelegram("****_bot", "Sensor1 become wet! Valves will be closed!")
			}
		}
	end
	
rule "Check watercount_temp2"
	when
		Item watercount_temp2 received update
	then
		if ((watercount_temp2.state as DecimalType) < 37 )
		{
			sendTelegram("****_bot", String::format("Hot water temp drop to %s", watercount_temp2.state.toString));
		}
	end

rule "Check watercount_pressure0"
	when
		Item watercount_pressure0 received update
	then
		if ((watercount_pressure0.state as DecimalType) < 1 && (watercount_pressure0.historicState(now.minusSeconds(3)).state as DecimalType) >= 1)
		{
			sendTelegram("****_bot", String::format("Cool pressure drop to %s", watercount_pressure0.state.toString));
		}
		if ((watercount_pressure0.state as DecimalType) > 1 && (watercount_pressure0.historicState(now.minusSeconds(3)).state as DecimalType) <= 1)
		{
			sendTelegram("****_bot", String::format("Cool pressure rise to %s", watercount_pressure0.state.toString));
		}
	end
rule "Check watercount_pressure1"
	when
		Item watercount_pressure1 received update
	then
		if ((watercount_pressure1.state as DecimalType) < 1 && (watercount_pressure1.historicState(now.minusSeconds(3)).state as DecimalType) >= 1)
		{
			sendTelegram("****_bot", String::format("Hot pressure drop to %s", watercount_pressure1.state.toString));
		}
		if ((watercount_pressure1.state as DecimalType) > 1 && (watercount_pressure1.historicState(now.minusSeconds(3)).state as DecimalType) <= 1)
		{
			sendTelegram("****_bot", String::format("Hot pressure rise to %s", watercount_pressure1.state.toString));
		}
	end

rule "Generate send string counters" //every 24 day of mounth  in 00.01 minutes
	when
		Time cron "0 0 1 24 1/1 ?" 
	then
		
		var float deltaCool = (watercount_count0.state as DecimalType).floatValue() - (watercount_sendCool.state as DecimalType).floatValue()
		var float deltaHot = (watercount_count1.state as DecimalType).floatValue() - (watercount_sendHot.state as DecimalType).floatValue()
		
		if (deltaCool >= 0 && deltaHot >= 0)
		{
			watercount_sendStr.postUpdate(String::format(" %.2f / %.2f 3", deltaCool, deltaHot))
			watercount_sendCool.state = watercount_count0.state
			watercount_sendHot.state = watercount_count1.state
			sendTelegram("****_bot", String::format(" 23,  5, . 23.  №2560097 (.) = %.2f 3. C №2538996 (.) = %.2f 3. %s", (watercount_sendCool.state as DecimalType).floatValue(), (watercount_sendHot.state as DecimalType).floatValue(), watercount_sendStr.state.toString()))
		}
		else
		{
			watercount_sendSwitch.postUpdate(0)
			sendTelegram("****_bot", "Current counters value less than sended last time. Turn off autosend.")
		}
end
rule "Send string counters" 
	when
		Time cron "0 0 23 24 1/1 ?"
	then
		if (watercount_sendSwitch.state == 1)
		{
			sendMail("uk@uk.ru", " 23,  5, . 23", String::format(" 23,  5, . 23.  №2560097 (.) = %.2f 3. C №2538996 (.) = %.2f 3", (watercount_sendCool.state as DecimalType).floatValue(), (watercount_sendHot.state as DecimalType).floatValue()));
			sendTelegram("****_bot", "Send email with watercount values");
		}
		else
		{
			sendTelegram("****_bot", "Can't send email with watercount values - autosend is OFF.");
		}
end
rule "Rotate valves" 
	when
		Time cron "0 0 05 25 1/1 ?"
	then
		if (watercount_valve0.state == 0 && watercount_valve1.state == 0)
		{	
			watercount_valve0.postUpdate(1)
			Thread::sleep(1000)
			watercount_valve1.postUpdate(1)
			Thread::sleep(1000)
			watercount_valve0.postUpdate(0)
			Thread::sleep(1000)
			watercount_valve1.postUpdate(0)
			sendTelegram("****_bot", "Valves was rotated.");
		}
		else
		{
			sendTelegram("****_bot", "Can't rotate valves, it's closed.");
		}
end


Para enviar mensajes, no utilizo la funcionalidad integrada de la aplicación de Android Openhab, ni me integro con su nube. Me gusta el bot de Telegram. Cómo configurar y conectar el bot se puede ver en la wiki . Para enviar cartas desde el buzón de correo de gmail, si tiene autenticación de dos factores, debe habilitar una contraseña única para la aplicación de correo y establecer esta contraseña en la configuración de openhab.

Camina por las reglas.

Comprobar sensor_cuenta_agua- el controlador envía nuevos valores del sensor de fugas solo cuando se cambia el valor o si hubo una falsa alarma (menos de 10 ciclos). Analizamos el significado histórico y surgido, formamos mensajes informativos. Hay un matiz, un intento de obtener prevoiusItem constantemente da el valor actual, no he encontrado una solución, tomo el valor "-3 segundos", si alguien lo superó, escríbalo en comentarios o en PM.

Comprobar watercount_temp2 : compruebe si hay menos de 37, lo que significa que el agua caliente se ha enfriado, debe encender el calentador de flujo a su llegada.

Compruebe watercount_pressure : analizamos el valor actual y el anterior, respondemos con un mensaje a una caída por debajo de 1 atm y un crecimiento por encima de él.

Generar contadores de cadena de envío- comienza en cron el 24 de cada mes a la 1 de la mañana. Verificamos que los valores ahora sean mayores que los enviados la última vez. Si es menos, apague el envío automático y genere una alerta. Si está bien, recordamos los valores de los contadores para enviar al Código Penal, enviamos el cuerpo del mensaje futuro al telegrama. Al mismo tiempo, ahorramos en watercount_sendStr cuánto consumimos durante el mes pasado.

Generar contadores de cadena de envío : comienza en cron el día 24 a las 23.00. Comprueba si el envío automático está habilitado, si está activado: el casco envía contadores al correo de la compañía de envío. Resulta que tengo el día 24 todo el día, algo para arreglar o simplemente reducir el envío automático, si se produjo un error en los telegramas.

Actualización por comentario . Rotar las válvulas- La regla para cerrar / abrir el grifo una vez al mes contra la ebullición. El día 25 a las 5 am, para no entrar en el trabajo del lavavajillas o la lavadora, pero incluso si llega allí, no es crítico, el agua se cerrará durante unos 3-4 segundos.

Y solo aquí comienza la casa inteligente ... La
combinación de sistemas en un solo punto (openhab) le permite construir una lógica que no está disponible para un conjunto de sistemas autónomos. Por ejemplo: ha llegado un evento de aumento en el medidor de agua: el sistema de seguridad está activo, las cerraduras de la puerta delantera están cerradas, el consumo de energía del lavavajillas y la lavadora es inferior a 5 vatios, lo que significa que se detecta una fuga por parte de los sensores. Formamos un comando para cerrar las grúas, enviamos un mensaje al bot en el Telegram. Pero esto es como un hilo más tarde.

Source: https://habr.com/ru/post/es399459/


All Articles