Blackjack e proteção contra vazamento

Saudações. Existe uma coisa - hydrolock \ neptune \ avkvastorozh - sistemas de fechamento de água se ocorrer um vazamento não controlado. O princípio é simples - um sensor de água + automação + um par de torneiras elétricas. Mas o diabo, como sempre, está nos detalhes: como as torneiras são arranjadas, como os sensores de vazamento são arranjados e por que um custa 50 rublos e o outro custa 500 rublos. Um quilo de boletim de layout será embrulhado em volta dessa coisa toda, a embalagem tirará seus olhos, etc.

Na história, vou percorrer os tijolos do sistema, que me guiaram na escolha. Todo o sistema é construído com sensores de fábrica e um controlador caseiro baseado no Particle (ex.Spark) Photon (como um esp8266 que possui um IDE na nuvem para cabeamento pronto), a base do dispositivo é o controlador stm + módulo wifi da Broadcom. Tudo isso está vinculado a um servidor openhab no Orange Pi One.



Por que não um sistema acabado?

- Porque eu posso fazer isso sozinho e é alto
- a integração com sistemas externos é ruim em sistemas prontos.
- Os sistemas prontos não possuem funções auxiliares - levando em consideração as leituras do medidor, sensores de temperatura da água, notificação de falta de água e outras fantasias eróticas ao caminhar.

Vamos começar com guindastes


Escolheu estupidamente na testa em termos de torque. Por algum tempo, ele viveu nos subúrbios, onde a qualidade da água (como provavelmente em todos os lugares de Zamkadye) deixa muito a desejar. Portanto, válvulas de esfera de 1/2 polegada se você não tocar no ano - é muito difícil girar. E no toalheiro aquecido de 1 polegada, nem tento movê-lo - apenas se fortalecer o ombro com uma chave inglesa, mas aqui você pode arrancar algo. O problema está nos depósitos de madeira de cálcio, "cobertos de vegetação" com uma palavra.

Consequentemente, a escolha recaiu sobre a série profissional do hydrolock - 21N * m de torque não parece uma tagarelice publicitária, o guindaste é simplesmente enorme - avalie o local de sua instalação antes da compra.



A torneira é selada, um selo de borracha ao redor do perímetro, a entrada sob a prensa-parafuso.
Retire a tampa.



À nossa frente está o topo do quadro e o motor de passo. Tudo isso é alimentado por 12 volts. Colocar o cabo de controle em aterramento coloca a válvula na posição fechada. No quadro, vemos um simples controlador PIC 12f629. Viveu, o controlador na movimentação do guindaste.

O verso do quadro é o mais interessante.



Driver shagovik L293 e fotoacoplador (emissor + fotodetector). Ela olha para a engrenagem principal da unidade, pintada em partes - branca e preta, fechada / aberta.



O guindaste gira o tempo todo em uma direção, a lógica do controlador é simples - torcemos o eixo até mudarmos para a cor desejada. A rotação do guindaste em uma direção é menos desgastante, e a maneira sem contato de determinar a posição tem menos probabilidade de causar azedo / mau funcionamento de um resistor ou reboque variável.

Para instalação, você pode desaparafusar o guindaste da unidade - ele é mantido em 2 porcas. Há uma junta de isolamento térmico entre a unidade e o guindaste.



Eu tive o reparo há um ano e meio atrás. O guindaste comprou há três anos - para desmontar, olhar para dentro, comprar mais e enrolar durante o reparo. Sim, agora ... o máximo que eu consegui neste circo infernal era colocar um coletor de sujeira na montagem do abastecimento de água com a perspectiva de substituí-lo por um guindaste.
E só depois de um ano e meio - comprei um segundo guindaste e ferrou-o.

Como resultado, observamos um fenômeno estranho e raro (lido na voz de Drozdov) - todas as informações do site do fabricante foram confirmadas. Além disso, a descrição é peculiar, como se os técnicos escrevessem, e o marketing fosse regado para as pessoas, mas poucas ainda entendem todas as fichas. Não há seção suficiente no site - para integradores com detalhes técnicos. Mesmo à custa do aumento de torque, eles não estavam no início - o guindaste no início está batendo com um motor de 1,5 A e após 2-3 segundos começa a subir no modo normal (corrente 0,7 A). Demora cerca de 25 a 30 segundos para fechar.

Outra experiência: às custas do torque - é excessivo para o tempo de Moscou, aqui a água é bastante boa; durante um ano e meio em um filtro de 100 μm, há um par de impurezas e sem excesso de crescimento. Você tem que pagar pelo grande torque com o preço, o horário de abertura e o local no armário. Eu acho que haverá unidades convencionais suficientes de Hydrolock Ultimate, Netuno ou Aquastorozh. Não posso garantir os dois últimos - não consegui entender, há cerca de 5 anos eles tinham engrenagens parcialmente plásticas, agora parecem ter consertado.

Existe também uma válvula hidrostática Winner com conexão direta de sensores ao inversor - isto é, se você não precisar de tudo o que eu fiz. Lá, a energia é autônoma a partir de 4 baterias e a base é semelhante a uma unidade definitiva. Em geral, também é potencialmente interessante para um controlador de fabricação própria - são 5 volts, você não precisa cercar dois barramentos de 5 e 12 volts e pode jogar fora o isolamento óptico.

Sensores de vazamento


Eu comprei os sensores do mesmo contador WSU - universal. Eles têm duas saídas de “coletor aberto”, uma puxa para o chão somente se houver água e a segunda - se a água entra, ela puxa para o chão o tempo todo até que a energia seja cortada. Eu uso apenas a primeira saída, o restante da lógica está no controlador, mas parece que essa saída pode ser útil para mais alguns sistemas de despacho de condomínio.

Os fios do kit estão a três metros em algum lugar. A cor dos fios é Ad_i_Israel. Confira a citação:

fio vermelho (marrom) (Vcc) alimentado de +5 a +30 volts.
fio preto (branco) (OUT2)
fio verde (OUT1)
fio amarelo (GND)

Aqui está o que impediu fazer terra branca / preta? A propósito, também no acionamento do guindaste, os fios de cor com lógica não são ale. O primeiro sensor está na cozinha, embaixo da pia ao lado da máquina de lavar louça.



O segundo no banheiro em uma vala de drenagem especial. Quando ele fez a mesa, ele não a trouxe para a parede. O resultado foi uma espécie de reservatório para coletar água do banheiro e do banheiro.



Por experiência operacional - já havia um alarme falso do sensor na máquina de lavar louça. A julgar pelo log de um ciclo de polling (500ms), havia um circuito, modificado o código - a mudança de estado agora ocorre com 10 valores idênticos consecutivos do sensor.

Os contatos do sensor são banhados a ouro. Um amigo tem sensores semelhantes há vários anos, nenhuma oxidação foi observada.

Sensores de pressão


Praticamente - showômetros. Precisão + - 0,5 atm me convinha completamente. Baseado nos sensores, um alerta de desligamento de água é fornecido. Eu comprei no Ali aqui .

Sensores de temperatura


E por que não adicionar? De útil - será capaz de informar uma vez por ano sobre o desligamento da água quente. Banal ds18b20 usado.

Contadores


O Itelma mais comum, uma vez a cada 10 litros faz contato. No lado do controlador, a saída é puxada para + 3,3v, o medidor a puxa para o chão.

Controlador




Dentro



Baseado no Particle Photon, mais detalhes aqui . Eles têm uma versão com um módulo 2G ou 3G (Electron). O primeiro firmware foi uma escória completa, piscando com diodos OK, mas assim que você começa salsichas complicadas em borracha, brincar com o i2c e interrompe pode perder o wifi. Agora você pode viver. Em princípio, você pode tirar os sensores de pressão do circuito e agitar tudo no ESP8266 - vá em frente. Primeiro, o fóton precisa estar vinculado à conta de partículas (feita pelo aplicativo no celular ou pelo console Particle CLI - eu uso apenas o segundo método) e registrar uma rede wifi. Após a ligação, aqui na seção do dispositivo aparece o controlador e seu status de conexão com a nuvem.



Eu tenho todos os nós conectados à nuvem apenas para atualizar o firmware. Não que eu fosse paranóico - apenas trabalhar com a nuvem não consome recursos ricos do controlador. O IDE suporta o trabalho com bibliotecas, literalmente uma dúzia é suportada pelo próprio contador, e o restante pela comunidade. Na minha observação, tudo o que é comum foi portado por um longo tempo, e outro recurso é que o IDE sabe imediatamente quantos projetos usam a biblioteca.

Código fonte do 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);
}


Através do MQTT, nos conectamos ao broker. Monitore os sensores e o capacete nos ramos correspondentes dos eventos e valores mqtt. Por exemplo, home / water_count / valve / 0 - uma unidade de água. home / número_de_água / contador / 0 - leituras do contador de água fria.

Assinamos os comandos para alterar o estado do inversor e definir o valor atual do contador (água fria e quente):

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

Há um botão no dispositivo - pressionando, ligamos a tela, desenhamos as leituras atuais de contadores, sensores e toques. Tela OLED, desaparecer rapidamente se ligado o tempo todo.

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

Este é um recurso interessante de hardware e software do controlador stm, na partícula de referência que eles chamam de BackupSRAM. O Photon possui uma saída vbat - isso não é energia da bateria ou carregamento. Enquanto houver tensão nesta perna, o conteúdo de 4k bytes de SRAM é mantido com o controlador completamente desenergizado. Isso elimina o problema de desgaste na EEPROM.

No código, as variáveis ​​que devem ser direcionadas para esta memória são declaradas com a indicação: retidas. Eu implementei o hardware do supercapacitor em 1.5F. De acordo com a ficha técnica, a memória morrerá em 1,6v, de acordo com meus experimentos de bancada na placa proto que ocorrerá em 2 semanas com aproximadamente o meu capacitor. A lógica de fechar os guindastes quando os sensores são acionados é "autônoma" e não depende da conexão com o openhab. Há um interruptor de três vias para controle direto da unidade - automático, OFF (torneiras abertas), Fechar (fechar).

O diagrama de circuito abaixo:



O projeto Eagle, juntamente com as bibliotecas personalizadas, pode ser baixado aqui .

A taxa foi feita LUT, nas faixas não encolheram.

Melhor amigo de banho de água LUT


Fonte de alimentação. Precisamos de 12 e 5 volts. O doador é procurado no ebay pela linha: "adaptador de energia do disco rígido 5v 12v", como este .

Habitação


Foi impresso com plástico PLA em uma impressora 3D (Tarantula Tevo). Bico 0,4 mm, camada 0,25 mm. A tampa é ao mesmo tempo e a base para a montagem da placa controladora. A base com a fonte de alimentação está conectada à parede. A base com a tampa não é presa com parafusos, há tensão suficiente na tampa (como a tampa da avó em frascos de geleia) e a estrutura em camadas das paredes funciona.

Modelo 3D no arquivo .





Veja como tudo parece montado em um cano de água.



Openhab


Implantado no Orange Pi One sob o Armbian.

Configuração do item
  -    ,  .
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]" }


Uma pequena regra de transformação é necessária para o sensor de vazamento.

Transformação de configurações
transform\water_sensor.map
1=dry
0=wet
undefined=undefined

Formamos a página para gerenciamento:

Configura o 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
}
}

E regras de processamento:

Configura regras
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 mensagens, não uso a funcionalidade interna do aplicativo android openhab, nem integro a nuvem deles. Eu gosto do bot do Telegram. Como configurar e conectar o bot pode ser visto no wiki . Para enviar cartas da caixa de correio do Gmail, se você tiver autenticação de dois fatores, será necessário habilitar uma senha de uso único para o aplicativo de email e definir essa senha na configuração do openhab.

Siga as regras.

Verifique watercount_sensor- o controlador envia novos valores de sensor de vazamento somente quando o valor é alterado ou se houve um alarme falso (menos de 10 ciclos). Analisamos o significado histórico e histórico, formamos mensagens informativas. Há uma nuance - uma tentativa de obter prevoiusItem constantemente fornece o valor atual, não encontrei uma solução - tomo o valor "-3 seg", se alguém o superou, anote-o nos comentários ou no PM.

Verifique watercount_temp2 - verifique se menos de 37, o que significa que a água quente ficou fria; você deve ligar o aquecedor de fluxo na chegada.

Verifique watercount_pressure - analisamos o valor atual e o anterior, respondemos com uma mensagem a uma queda abaixo de 1 atm e crescimento acima dele.

Gere contadores de string de envio- começa no cron no dia 24 de cada mês às 13 horas da manhã. Verificamos que os valores agora são maiores que os enviados da última vez. Se menor, desative o envio automático e gere um alerta. Se estiver bem - lembramos os valores dos contadores para enviar ao Código Penal, enviamos o corpo da mensagem futura para o telegrama. Ao mesmo tempo, economizamos em watercount_sendStr quanto consumimos no mês passado.

Gerar contadores de string de envio - inicia no cron no 24º dia às 23h00. Verifica se o envio automático está ativado, se está ativado - o capacete envia contadores para o correio da transportadora. Acontece que eu tenho o 24º dia o dia todo, algo para corrigir ou simplesmente reduzir o envio automático, se ocorrer um erro nos telegramas.

Atualize por comentário . Rodar válvulas- A regra para fechar / abrir a torneira uma vez por mês contra a fervura. Dia 25 às 5h - para não entrar no trabalho da máquina de lavar louça ou da máquina de lavar, mas mesmo que chegue lá - não é crítico, a sobreposição de água será de cerca de 3-4 segundos.

E somente aqui começa a casa inteligente ... A
combinação de sistemas em um único ponto (openhab) permite criar uma lógica que não está disponível para um conjunto de sistemas autônomos. Por exemplo: chegou um evento de aumento no hidrômetro - o sistema de segurança está ativo, as travas da porta da frente estão fechadas, o consumo de energia da máquina de lavar louça e da lavadora de roupas é inferior a 5 watts - o que significa que um vazamento pelos sensores é detectado. Formamos um comando para fechar os guindastes, enviar uma mensagem para o bot no Telegram. Mas isso é como um fio depois.

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


All Articles