📜 ⬆️ ⬇️

Blackjack leakage protection with counters

Greetings. There is such a thing - skidrok \ neptune \ avkvastorozh - the system overlapping the water supply, if there is no controlled leakage. The principle is simple - water sensor + automatic + a couple of taps with electric drives. But the devil, as usual, is in the details: how are the taps arranged, how the leakage sensors are arranged, and why one costs 50 rubles, and the other 500r. A whole kilogram of breading bread will be put on the whole thing, packaging will be torn out, etc.

In the story I will go through the bricks of the system, what guided the choice. The whole system is built on factory sensors and a homemade controller based on Particle (ex.Spark) Photon (such esp8266 which has a cloudy IDE for wiring out of the box), the base of the device stm controller + wifi module from the brodkom. All this is tied to an openhab server on the Orange Pi One.



Why not a complete system?
')
- Because I can myself and this is a thrill
- In ready-made systems, integration with external systems is lame.
- Ready systems do not have auxiliary functions - taking into account meter readings, water temperature sensors, notification of water shutdowns and other pedestrian erotic fantasies.

Nachem with taps


Chose stupidly in the forehead for torque. For some time he lived in the suburbs, where the quality of water (as probably everywhere in the castle) leaves much to be desired. So ball valves for 1/2 inches if the year does not touch - turn very hard. And on the heated towel rail 1 inch, I don’t even try to move it - only if I strengthen the shoulder with an adjustable wrench, and here I can break something. The problem is in calcium deposits, “overgrown” with the same word.

Correspondingly, the choice fell on the professional series from the hydrolock - 21N * m of torque does not feel like an advertisement chatter, the crane is simply huge - estimate the place of its installation before purchase.



The crane is sealed, around the perimeter of the rubber seal, the entrance under the screw seal.
Remove the cover.



Before us is the top of the board and the stepper motor. It eats all this from 12 volts. Closing the control cable to ground puts the valve in the closed position. On the board we see a simple PIC 12f629 controller. Lived, the controller in the drive of the crane.

The back of the board is the most interesting.



L293 driver shagovika and photopairs (emitter + photodetector). She looks at the main drive gear, which is painted into parts - white and black, closed / open.



The crane rotates all the time in one direction, the logic of the controller is simple - we twist the shaft until we switch to the desired color. Rotation of the crane in one direction, it is less wear, and the contactless method of determining the position - less chance of souring \ failure variable resistor or limit switch.

For installation, you can unscrew the valve from the drive - held on 2 nuts. Between the drive and the crane heat-insulating laying.



I had a repair a year and a half ago. The crane bought about three years ago - to disassemble, to look inside, to buy more and to wind during repair. Yeah, now ... the maximum that I managed to do in this hellish circus was to lay a mud trap in the water distribution assembly with the prospect of replacing it with a faucet.
And only a year and a half later, I bought a second crane and screwed them up.

As a result, we observe a strange and rare phenomenon (read by Drozdov’s voice) - all information from the manufacturer’s website has been confirmed. Moreover, the description is peculiar, as if the techies were writing, and then the marketing was polished for the people, but still few would understand all the chips. Not enough section on the site - for integrators with technical details inside. Even at the expense of increased torque at the start, they did not lie - the crane at the start started up with a 1.5A engine and after 2-3 seconds it began to fade in normal mode (current 0.7 A). It takes 25-30 seconds to close.

Even from experience: at the expense of torque - it is redundant for Moscow time, here the water is quite OK, for a year and a half in a 100 micron filter a pair of scales and no overgrowth. For a large torque, you have to pay with price, opening time, and space in the cabinet. I think here the usual drives enough from the Hidrolok Ultimate, Neptune or Aquastorozha. For the last two I can’t vouch - I didn’t understand it, about 5 years ago they had partially plastic gears, now they seem to have fixed it.

There is still a winner skirok with direct connection of sensors to the drive - this is if you do not need all that I did. There, the power is autonomous from 4 batteries, and the base seems to be from the ultimate drive. In general, it is potentially interesting for the self-made controller - power is 5 volts, it is not necessary to fence two buses for 5 and 12 volts and you can throw an optocoupler.

Leakage sensors


I bought the sensors of the same WSU counterparts - universal. They have two open "open collector" outlets, one pulls on the ground only if there is water, the second one - if the water is dropped, it pulls on the ground all the time until the power is cut. Only the first output I use, the rest of the logic in the controller, but it seems that this output can be useful for some more kondovyh systems of distribution control.

The wires in the kit are three meters somewhere. The color of the wires - Ad_i_Israel. Check out this quote:

red (brown) wire (Vcc) powered from +5 to +30 volts.
black (white) wire (OUT2)
green wire (OUT1)
yellow wire (GND)

Is that what prevented white / black earth from doing so? On the drive of the crane, too, by the way, the wires are color-coded with no logic. The first sensor is in the kitchen, under the sink next to the dishwasher.



Second in the bathroom in a special drainage ditch. When did the screed - did not bring it to the wall. It turned out a sort of sump for collecting water from the bathroom and toilet.



From operating experience - I already had one false triggering of the sensor in the dishwasher. Judging by the log for one polling cycle (500ms), there was a closure, modified code - the state change now occurs at 10 consecutive identical values ​​from the sensor.

The sensor contacts are gilded. Comrade similar sensors for several years, oxidation is not observed.

Pressure Sensors


Almost - show meters. Accuracy + - 0.5 ATM completely suited me. Based on the sensor comes a warning about turning off the water. I bought on Ali here .

Temperature sensors


Why not add it? From useful - can once a year notify about turning off the hot water. Used commonplace ds18b20.

Counters


The most common Itelma, once in 10 liters, close the contacts. On the controller side, the output is pulled to + 3.3v, the meter pulls it to the ground.

Controller




Inside



Based on Particle Photon, more here . They have a version with 2G or 3G module (Electron). The first firmware was a complete slag, blinking OK with diodes, but as soon as you start to hard-fake, playing with i2c and interruptions can lose wifi. Now you can live. In principle, you can throw the pressure sensors out of the circuit and stir everything up on the ESP8266 - dare. First of all, photon should be tied to the particle account (done via the App on a mobile phone or via the Particle CLI console - I use only the second method) and register a wifi network. After binding, a controller appears here in the device section and its connection status to the cloud.



All my nodes connect to the cloud only to update the firmware. Not that I'm paranoid - just working with the cloud is eating not the rich resources of the controller. IDE supports work with libraries, literally a dozen are supported by the counter, the rest are supported by the community. According to my observation, everything common has been ported for a long time, another trick - at once in the IDE, how many projects use the library.

Firmware source code
// 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); } 


Through MQTT we connect to the broker. We monitor sensors and a helmet in the corresponding branches of mqtt events and values. For example home / water_count / valve / 0 is a cold water drive. home / water_count / counter / 0 - cold water meter readings.

We subscribe to commands to change the status of the drive and set the current value of the meter (cold and hot water):

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

There is one button on the device - by pressing we turn on the screen, we draw the current readings of counters, sensors and taps. OLED screen, quickly fade if done on all the time.

 STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY)); 

This is an interesting software and hardware feature of the stm controller; in the reference Particle it is called BackupSRAM. Photon has a vbat pin - it's not battery power and not charging. As long as there is voltage on this leg, the contents of 4kbyte SRAM are maintained when the controller is completely de-energized. This eliminates the problem of EEPROM wear.

In the code, variables that need to be driven into this memory are declared with the indication: retained. In hardware, I implemented a 1.5F feed from a supercapacitor. According to the datasheet, the memory will die at 1.6v, according to my poster experiments on the protoboard, it will come in 2 weeks with about my capacitor. The logic of closing the taps when the sensors are triggered is “autonomous” and does not depend on the connection to openhab. There is a 3-way direct drive control switch - automatics, OFF (open taps), Close (close).

The circuit board below:



The Eagle project together with custom libs can be downloaded here .

The fee was made LUT, in the tracks is not small.

Water bath LUT's best friend


Power Supply. We need 12 and 5 volts. The donor is searched on ebay on the line: "hard drive power adapter 5v 12v", such as this .

Housing


Printed with PLA plastic on a 3d printer (Tarantula Tevo). Nozzle 0.4mm, layer 0.25mm. The cover is also the base for mounting the controller board. Base with power supply is attached to the wall. The base with the lid is not fastened with screws, the lid tension is enough (like that of the grandmother of the lid on the jars of jam) and the layered structure of the walls works.

3D model in the archive .





Here's how it all looks mounted on the water distribution.



Openhab


Deployed to Orange Pi One under Armbian.

Config 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]" } 


You need a small transformation rule for leakage sensor readings.

Transform configs
transform \ water_sensor.map
1 = dry
0 = wet
undefined = undefined

We create a page to manage:

Sitemap configs
Sitemap
Text label = "Water Treatment" 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
}
}

And processing rules:

Rules configs
 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 


To send messages, I do not use the built-in functionality of the android openhab application, as well as integrating with their cloud. I like the Telegram bot. How to configure and enable bots can be found on the wiki . To send emails from the gmail mailbox, if you have two-factor authentication, you need to enable a one-time password for the email application and set this particular password in the openhab config.

Walk by the rules.

Check watercount_sensor - the controller sends new leakage sensor values ​​only when changing the value or if there was a false positive (less than 10 cycles). We analyze the incoming and historical significance, form informational messages. There is a nuance - an attempt to get prevoiusItem constantly gives the current value, did not find a solution - I take the value "-3 sec", if someone overcame - write to comments or in a personal.

Check watercount_temp2 - we check, if it is less than 37, it means that the hot water has become cold, it is necessary to turn on the flow-through heater upon arrival.

Check watercount_pressure - we analyze the current and previous value, react with a message to drop below 1 atm and to rise above it.

Generate send string counters - starts on cron on the 24th day of each month at 1 am. We check that the values ​​are now higher than those sent last time. If less - turn off auto send and create an alert. If OK - remember the values ​​of the counters to be sent to the Criminal Code, send the future letter body to the telegrams. At the same time in watercount_sendStr we save how much we have consumed over the past month.

Generate send string counters - starts on cron on the 24th at 23.00. Checks if auto-send is on, if on - a counter of the value of the counters is sent to the control company mail. It turns out that I have 24 numbers all day, something to fix or just cut down auto-send if an error has arrived in the telegrams.

Update by comment . Rotate valves - a rule for closing / opening the tap once a month against boiling. On the 25th at 5 am, in order not to get to the work of the dishwasher or the washer, but even if it gets in, it is not critical, the overlap of the water will be about 3-4 seconds.

And only here begins the smart home ...
Combining systems in a single point (openhab) allows you to build logic that is not accessible to a set of autonomous systems. For example: an event of an increase in the water meter came - the security system is active, the entrance door locks are closed, the electricity consumption of the dishwasher and the washer is less than 5 W - it means that there is a leakage past the sensors. We form a team to close the cranes, send a message to the bot in Telegram. But this is like a thread later.

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


All Articles