Blackjack et protection contre les fuites

Salutations. Il y a une telle chose - hydrolock \ neptune \ avkvastorozh - des systèmes d'arrêt d'eau en cas de fuite incontrôlée. Le principe est simple - un capteur d'eau + automatisation + une paire de robinets électriques. Mais le diable, comme d'habitude, est dans les détails: comment les robinets sont disposés, comment les capteurs de fuite sont disposés, et pourquoi l'un coûte 50 roubles et l'autre coûte 500 roubles. Un kilogramme d'un bulletin de mise en page sera enroulé autour de tout cela, l'emballage vous enlèvera les yeux, etc.

Dans l'histoire, je vais parcourir les briques du système, qui m'ont guidé dans le choix. L'ensemble du système est construit sur des capteurs d'usine et un contrôleur fait maison basé sur des particules (ex.Spark) Photon (tel qu'un esp8266 qui a un IDE cloud sur le câblage hors de la boîte), la base de l'appareil est un contrôleur stm + module wifi de Broadcom. Tout cela est lié à un serveur openhab sur Orange Pi One.



Pourquoi pas un système fini?

- Parce que je peux le faire moi-même et c'est élevé
- L'intégration avec des systèmes externes est boiteuse dans des systèmes prêts à l'emploi.
- Les systèmes prêts à l'emploi n'ont pas de fonctions auxiliaires - prenant en compte les relevés de compteurs, les capteurs de température de l'eau, la notification des pannes d'eau et autres fantasmes érotiques de marche.

Commençons par les grues


A choisi bêtement au front en termes de couple. Pendant quelque temps, il a vécu en banlieue, où la qualité de l'eau (comme probablement partout à Zamkadye) laisse beaucoup à désirer. Donc, les vannes à bille 1/2 pouce si vous ne touchez pas l'année - il est très difficile de tourner. Et sur le porte-serviettes chauffant de 1 pouce, je n'essaie même pas de le déplacer - seulement si je renforce l'épaule avec une clé, mais ici vous pouvez arracher quelque chose. Le problème réside dans les dépôts de bois dur calcique, "envahis" par un seul mot.

En conséquence, le choix s'est porté sur la série professionnelle de l'hydrolock - 21N * m de couple ne ressemble pas à un bavardage publicitaire, la grue est tout simplement énorme - évaluez le lieu de son installation avant l'achat.



Le robinet est scellé, un joint en caoutchouc autour du périmètre, l'entrée sous le presse-étoupe.
Retirez le couvercle.



Devant nous se trouve le haut de la planche et le moteur pas à pas. Tout cela est alimenté par 12 volts. La mise en court-circuit du câble de commande à la terre met la vanne en position fermée. Sur la carte, nous voyons un simple contrôleur PIC 12f629. Survécu, le contrôleur dans le lecteur de grue.

Le dos de la planche est le plus intéressant.



Pilote shagovik L293 et ​​photocoupleur (émetteur + photodétecteur). Elle regarde l'engrenage principal de l'entraînement, qui est peint en plusieurs parties - blanc et noir, fermé / ouvert.



La grue tourne tout le temps dans une direction, la logique du contrôleur est simple - nous tournons l'arbre jusqu'à ce que nous passions à la couleur souhaitée. La rotation de la grue dans une direction est moins d'usure et la manière sans contact de déterminer la position est moins susceptible de provoquer un dysfonctionnement / une défaillance d'une résistance variable ou d'un interrupteur de fin de course.

Pour l'installation, vous pouvez dévisser la grue de l'entraînement - elle est maintenue sur 2 écrous. Il y a un joint calorifuge entre l'entraînement et la grue.



J'ai eu la réparation il y a un an et demi. La grue achetée il y a trois ans - pour démonter, regarder à l'intérieur, acheter plus et l'enrouler pendant la réparation. Ouais, maintenant ... le maximum dont j'ai réussi dans ce cirque infernal était de poser un collecteur de saletés dans l'assemblage de l'alimentation en eau avec la perspective de le remplacer par une grue.
Et seulement après un an et demi - j'ai acheté une deuxième grue et je les ai vissées.

En conséquence, nous observons un phénomène étrange et rare (lu dans la voix de Drozdov) - toutes les informations du site Web du fabricant ont été confirmées. De plus, la description est particulière, comme si les techniciens l'avaient écrit, puis le marketing était arrosé pour les gens, mais encore peu de gens comprennent toutes les puces. Il n'y a pas assez de section sur le site - pour les intégrateurs avec des détails techniques à l'intérieur. Même au détriment de l'augmentation du couple, ils ne se sont pas allongés au début - la grue au démarrage frappe avec un moteur de 1,5 A et après 2-3 secondes, elle commence à monter en mode normal (courant 0,7 A). La fermeture prend environ 25 à 30 secondes.

Autre expérience: au détriment du couple - c'est excessif pour l'époque de Moscou, ici l'eau est tout à fait OK, pendant un an et demi dans un filtre de 100 μm il y a une paire de crasses et pas de prolifération. Il faut payer le gros couple avec le prix, l'heure d'ouverture et la place dans le placard. Je pense qu'il y aura suffisamment de disques conventionnels Hydrolock Ultimate, Neptune ou Aquastorozh. Je ne peux pas garantir les deux derniers - je ne l'ai pas compris, il y a environ 5 ans, ils avaient des engrenages partiellement en plastique, maintenant ils semblent l'avoir réparé.

Il y a aussi un hydrolock Winner avec connexion directe de capteurs au variateur - c'est si vous n'avez pas besoin de tout ce que j'ai fait. Là, la puissance est autonome à partir de 4 batteries, et la base est similaire à un lecteur ultime. En général, il est également potentiellement intéressant pour un contrôleur self-made - c'est 5 volts, vous n'avez pas besoin de mettre deux bus sur 5 et 12 volts et vous pouvez jeter l'isolement optique.

Capteurs de fuite


J'ai acheté les capteurs du même compteur WSU - universel. Ils ont deux sorties «collecteur ouvert», l'une tire au sol uniquement s'il y a de l'eau, la seconde - si de l'eau pénètre, elle tire au sol tout le temps jusqu'à ce que l'alimentation soit coupée. J'utilise uniquement la première sortie, le reste de la logique est dans le contrôleur, mais il semble que cette sortie puisse être utile pour d'autres systèmes de répartition de condos.

Les fils du kit sont à trois mètres quelque part. La couleur des fils est Ad_i_Israel. Découvrez le devis:

fil rouge (marron) (Vcc) alimenté de +5 à +30 volts.
fil noir (blanc) (OUT2)
fil vert (OUT1)
fil jaune (GND)

Voici ce qui a empêché de faire de la terre blanche / noire? Soit dit en passant, sur la grue, les fils de couleur avec logique ne sont pas ale. Le premier capteur se trouve dans la cuisine, sous l'évier à côté du lave-vaisselle.



Le deuxième dans la salle de bain dans un fossé de drainage spécial. Quand il a fait la chape, il ne l'a pas collée au mur. Le résultat fut une sorte de puisard pour recueillir l'eau de la salle de bain et des toilettes.



De l'expérience de fonctionnement - il y avait déjà une fausse alarme du capteur dans le lave-vaisselle. A en juger par le journal pour un cycle d'interrogation (500 ms), il y avait un circuit, a modifié le code - le changement d'état se produit maintenant à 10 valeurs identiques consécutives du capteur.

Les contacts du capteur sont plaqués or. Un ami possède des capteurs similaires depuis plusieurs années, aucune oxydation n'a été constatée.

Capteurs de pression


Pratiquement - showomètres. La précision + - 0,5 atm me convenait parfaitement. Sur la base des capteurs, une alerte d'arrêt d'eau survient. J'ai acheté Ali ici .

Capteurs de température


Et pourquoi ne pas ajouter? D'utile - il pourra une fois par an vous informer de l'arrĂŞt de l'eau chaude. Banal DS18B20 d'occasion.

Compteurs


Itelma le plus courant, une fois tous les 10 litres, prenez contact. Côté contrôleur, la sortie est tirée jusqu'à + 3,3v, le compteur la tire au sol.

ContrĂ´leur




À l'intérieur



Basé sur Particle Photon, plus de détails ici . Ils ont une version avec un module 2G ou 3G (Electron). Le premier micrologiciel était un laitier complet, clignotant avec des diodes OK, mais dès que vous commencez une saucisse compliquée à mâcher, jouer avec i2c et les interruptions peut perdre le wifi. Maintenant tu peux vivre. En principe, vous pouvez jeter les capteurs de pression hors du circuit et remuer tout sur l'ESP8266 - allez-y. Tout d'abord, photon doit être lié au compte de particules (fait via l'application sur le mobile ou via la console CLI Particle - j'utilise uniquement la deuxième méthode) et enregistrer un réseau wifi. Après la liaison, ici dans la section appareil apparaît le contrôleur et son état de connexion au cloud.



J'ai tous les nœuds connectés au cloud uniquement pour mettre à jour le firmware. Non pas que je serais paranoïaque - travailler avec le cloud ne consomme pas de riches ressources de contrôleur. L'IDE prend en charge le travail avec les bibliothèques, littéralement une douzaine sont prises en charge par le compteur lui-même, le reste par la communauté. À mon avis, tout ce qui est commun a été porté depuis longtemps, et une autre caractéristique est que l'EDI sait immédiatement combien de projets utilisent la bibliothèque.

Code source du 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);
}


Grâce à MQTT, nous nous connectons au courtier. Surveillez les capteurs et le casque dans les branches correspondantes des événements et des valeurs mqtt. Par exemple home / water_count / valve / 0 - un lecteur d'eau. home / water_count / counter / 0 - lectures du compteur d'une eau froide.

Nous souscrivons aux commandes pour changer l'état du variateur et régler la valeur actuelle du compteur (eau froide et eau chaude):

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

Il y a un bouton sur l'appareil - en appuyant sur nous allumons l'écran, dessinons les lectures actuelles des compteurs, des capteurs et des robinets. Écran OLED, s'estompe rapidement s'il est allumé tout le temps.

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

Il s'agit d'une fonctionnalité matérielle et logicielle intéressante du contrôleur stm, dans la particule de référence qu'ils appellent BackupSRAM. Photon a une sortie vbat - ce n'est pas la puissance de la batterie ou la charge. Tant qu'il y a de la tension sur cette branche, le contenu de 4 000 octets de SRAM est maintenu avec le contrôleur complètement hors tension. Cela élimine le problème d'usure sur l'EEPROM.

Dans le code, les variables qui doivent être introduites dans cette mémoire sont déclarées avec l'indication: conservées. J'ai implémenté le matériel du supercondensateur à 1.5F. Selon la fiche technique, la mémoire mourra à 1.6v, selon mes expériences de banc sur le proto-board, elle viendra dans 2 semaines avec environ mon condensateur. La logique de fermeture des grues lorsque les capteurs sont déclenchés est "autonome" et ne dépend pas de la connexion à openhab. Il y a un interrupteur à 3 voies pour le contrôle direct de l'entraînement - automatique, OFF (robinets ouverts), Close (fermé).

Le schéma ci-dessous:



Le projet Eagle, ainsi que des bibliothèques personnalisées, peuvent être téléchargés ici .

La redevance a été faite LUT, dans les pistes n'a pas diminué.

Bain d'eau meilleur ami de LUT


Bloc d'alimentation. Nous avons besoin de 12 et 5 volts. Le donateur est recherché sur ebay pour la ligne: "adaptateur de disque dur 5v 12v", comme celui- ci .

Logement


Il a été imprimé avec du plastique PLA sur une imprimante 3D (Tarantula Tevo). Buse 0,4 mm, couche 0,25 mm. Le couvercle est en même temps et la base pour le montage de la carte contrôleur. La base avec l'alimentation est fixée au mur. La base avec le couvercle n'est pas fixée avec des vis, la tension du couvercle est suffisante (comme le couvercle de la grand-mère sur les pots de confiture) et la structure en couches des murs fonctionne.

Modèle 3D dans l' archive .





Voici à quoi tout cela ressemble monté sur une conduite d'eau.



Openhab


Déployé sur Orange Pi One sous Armbian.

Configuration de l'article
  -    ,  .
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]" }


Une petite règle de transformation est nécessaire pour le capteur de fuite.

Les configurations se transforment
transform\water_sensor.map
1=dry
0=wet
undefined=undefined

Nous formons la page de gestion:

Plan du site de configuration
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
}
}

Et les règles de traitement:

Règles de configuration
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


Pour envoyer des messages, je n'utilise pas la fonctionnalité intégrée de l'application Android openhab, ni ne m'intègre à leur cloud. J'aime le bot Telegram. Comment configurer et connecter le bot peut être vu sur le wiki . Pour envoyer des lettres à partir de la boîte aux lettres gmail, si vous disposez d'une authentification à deux facteurs, vous devez activer un mot de passe à usage unique pour l'application de messagerie et définir ce mot de passe dans la configuration openhab.

Suivez les règles.

Vérifiez watercount_sensor- le contrôleur envoie de nouvelles valeurs de capteur de fuite uniquement lorsque la valeur est modifiée ou en cas de fausse alarme (moins de 10 cycles). Nous analysons la signification historique et historique, nous formons des messages d'information. Il y a une nuance - une tentative pour obtenir prevoiusItem donne constamment la valeur actuelle, je n'ai pas trouvé de solution - je prends la valeur "-3 sec", si quelqu'un l'a surmontée, écrivez-la dans des commentaires ou dans PM.

Vérifiez watercount_temp2 - vérifiez si moins de 37, ce qui signifie que l'eau chaude est devenue froide, vous devez allumer le chauffe-eau à l'arrivée.

Vérifiez watercount_pressure - nous analysons la valeur actuelle et précédente, nous répondons par un message à une baisse en dessous de 1 atm et à une croissance au-dessus.

Générer des compteurs de chaînes d'envoi- commence le cron le 24 de chaque mois à 1 heure du matin. Nous vérifions que les valeurs sont désormais supérieures à celles envoyées la dernière fois. Si moins, désactivez l'envoi automatique et générez une alerte. Si tout va bien - nous nous souvenons des valeurs des compteurs à envoyer au Code pénal, nous envoyons le futur corps du message au télégramme. Dans le même temps, nous économisons dans watercount_sendStr combien nous avons consommé au cours du mois dernier.

Générer des compteurs de chaînes d'envoi - commence sur cron le 24e jour à 23h00. Vérifie si l'envoi automatique est activé, s'il est activé - le casque envoie des compteurs au courrier de la compagnie maritime. Il s'avère que j'ai le 24ème jour toute la journée, quelque chose à réparer ou simplement à couper l'envoi automatique, si une erreur s'est produite dans les télégrammes.

Mise à jour par commentaire . Vannes rotatives- La règle de fermeture / ouverture du robinet une fois par mois contre l'ébullition. Le 25 à 5h du matin - pour ne pas entrer dans le travail du lave-vaisselle ou du lave-linge, mais même s'il y arrive - ce n'est pas critique, le chevauchement de l'eau sera d'environ 3-4 secondes.

Et ce n'est qu'ici que la maison intelligente commence ... La
combinaison de systèmes en un seul point (openhab) vous permet de créer une logique qui n'est pas disponible pour un ensemble de systèmes autonomes. Par exemple: un événement d'augmentation du compteur d'eau est arrivé - le système de sécurité est actif, les serrures de la porte d'entrée sont fermées, la consommation d'énergie du lave-vaisselle et du lave-linge est inférieure à 5 watts - ce qui signifie qu'une fuite par les capteurs est détectée. Nous formons une commande pour fermer les grues, envoyons un message au bot dans le télégramme. Mais c'est comme un fil plus tard.

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


All Articles