二十一点和柜台漏电保护

问候。有这样的事情-Hydrolock \ neptune \ avkvastorozh-如果发生不可控制的泄漏,则将水切断系统。原理很简单-水传感器+自动化+一对电动龙头。但是魔鬼仍然像往常一样在细节上:水龙头如何布置,泄漏传感器如何布置以及为什么一个花费50卢布而另一个花费500卢布。一公斤的布局公告将被包裹在整件事上,包装将使您的视线移开,等等。

在故事中,我将逐步介绍系统的各个方面,这些内容引导了我的选择。整个系统基于工厂传感器和基于粒子(例如火花)光子的自制控制器(例如esp8266,具有开箱即用的基于云的IDE),设备基础是Broadcom的stm控制器+ wifi模块。所有这些都与Orange Pi One上的openhab服务器绑定在一起。



为什么不完成系统?

-因为我自己就能做到,而且
非常出色-在现成的系统中,与外部系统的集成比较滞后。
-现成的系统不具有辅助功能-考虑仪表读数,水温传感器,停水通知和其他步行色情幻想。

让我们从起重机开始


就扭矩而言,笨拙地选择额头。有一段时间,他住在郊区,那里的水质(很可能是扎姆卡迪(Zamkadye)的每个地方)都令人望而却步。因此,如果您不接触年份,则球阀为1/2英寸-很难转动。而且在1英寸加热毛巾架上,我什至不尝试移动它-仅当我用扳手加固肩膀时才可以,但在这里您可以将东西撕掉。问题在于钙硬木的沉积物,一个词“长满了”。

因此,从液压锁的专业系列中选择-21N * m的扭矩感觉并不像广告中所说的那样,起重机只是巨大的-在购买前评估其安装位置。



水龙头是密封的,周围是橡胶密封圈,螺纹密封套下方的入口。
取下盖子。



在我们面前的是电路板和步进电机的顶部。所有这些都由12伏特供电。将控制电缆接地会导致阀门处于关闭位置。在板上,我们可以看到一个简单的PIC 12f629控制器。住了,控制器在吊车上驱动。

板的背面是最有趣的。



L293 Shagovik驱动器和光电耦合器(发射器+光电探测器)。她看着驱动器的主齿轮,该齿轮涂了白色和黑色的部分,已闭合/打开。



起重机一直沿一个方向旋转,控制器的逻辑很简单-我们扭转轴,直到切换到所需的颜色。起重机在一个方向上的旋转磨损较小,并且非接触式确定位置的方式不太可能使可变电阻器或限位开关变酸/故障。

安装时,您可以从驱动器上拧下起重机-它固定在2个螺母上。驱动器和起重机之间有一个隔热垫片。



一年半前,我进行了维修。该起重机是三年前购买的,用于拆卸,查找内部,购买更多并在维修期间进行缠绕。是的,现在……在这种地狱般的马戏团中,我最大的努力就是在集水器中放置一个集尘器,并有望用起重机代替它。
仅仅一年半之后-我买了第二台起重机并拧紧。

结果,我们发现了一种奇怪且罕见的现象(用Drozdov的声音朗读)-来自制造商网站的所有信息均得到确认。而且,描述很奇特,就像技术专家写的那样,然后市场营销为人们所用,但仍然很少有人了解所有芯片。该站点上没有足够的部分–包含内部技术细节的集成商。即使以增加扭矩为代价,它们也不是一开始就处于撒谎状态–起步时起重机以1.5A的发动机撞击,并在2-3秒后开始以正常(当前的0.7 A)模式上升。关闭大约需要25-30秒。

另一个经验:以扭矩为代价-在莫斯科时间这是多余的,这里的水还可以,在100μm过滤器中放置一年半,有渣并且没有过度生长。您必须为价格,开启时间和壁橱中的位置付出巨大的代价。我认为将会有来自Hydrolock Ultimate,Neptune或Aquastorozh的常规驱动器。我不能保证最后两个-我没有弄清楚,大约5年前他们有部分塑料齿轮,现在似乎已经修复了。

还有一个Winner Hydrolock,可以将传感器直接连接到驱动器上-这是如果您不需要我做的所有事情。那里的动力来自4个电池,其底座类似于终极驱动器。通常,对于自制的控制器来说,这也可能很有趣-它是5伏特,您不需要在5伏特和12伏特上放置两条总线,就可以丢弃光隔离。

泄漏传感器


我购买了同一WSU计数器的传感器-通用。它们有两个“集电极开路”输出,一个只有在有水时才拉到地面,第二个-如果有水进来,它就一直拉到地面直到断电。我仅使用第一个输出,其余逻辑在控制器中,但看起来此输出对于更多的公寓分发系统很有用。

套件中的电线在某处三米远。电线的颜色是Ad_i_Israel。查看报价:

红色(棕色)线(Vcc)由+5到+30伏供电。
黑色(白色)线(OUT2)
绿色线(OUT1)
黄色线(GND)

阻止白色/黑色地球的原因是什么?顺便说一下,在起重机驱动器上,带有逻辑颜色的电线也不是稀疏的。第一个传感器在厨房里,在洗碗机旁边的水槽下面。



第二个在浴室的特殊排水沟中。当他做熨平板时,他没有把它拖到墙上。结果产生了一种用于从浴室和卫生间收集水的集水槽。



根据操作经验-洗碗机中的传感器已经出现了一次误报。通过日志判断一个轮询周期(500毫秒),有一个电路,修改了代码-状态更改现在从传感器发出的连续10个相同值处发生。

传感器触点镀金。一位朋友使用类似的传感器已有几年了,没有发现氧化现象。

压力传感器


实际上-照度计。精度+-0.5 atm完全适合我。根据这些传感器,会出现断水警报。我在这里买了阿里

温度传感器


为什么不添加呢?从实用开始-它将能够每年一次通知有关热水关闭的信息。二手的banal ds18b20。

专柜


最常见的Itelma,每10升接触一次。在控制器端,输出上拉至+ 3.3v,电表将其接地。

控制者




里面



基于粒子光子,更多细节请点击这里。它们具有带2G或3G模块(电子)的版本。第一个固件是完整的炉渣,并用OK二极管闪烁,但是一旦您咀嚼复杂的香肠,玩i2c和中断就会失去wifi。现在可以住了。原则上,您可以将压力传感器从电路中扔出,并搅动ESP8266上的所有部件,然后继续进行。首先,光子需要链接到粒子帐户(通过移动设备上的应用程序或通过粒子CLI控制台完成-我仅使用第二种方法)并注册wifi网络。绑定后,设备部分将在此处显示控制器及其连接到云的状态。



我已将所有节点连接到云,仅用于更新固件。并不是说我会偏执-仅与云一起工作会消耗不丰富的控制器资源。IDE支持使用库,实际上计数器本身支持一打,其余的则由社区支持。根据我的观察,所有常见的东西都已经移植了很长时间,并且另一个功能是IDE可以立即知道有多少项目在使用该库。

固件源代码
// 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);
}


通过MQTT,我们连接到代理。在mqtt事件和值的相应分支中监视传感器和头盔。例如home / water_count /阀门/ 0-水驱动器。home / water_count /计数器/ 0-冷水计数器的读数。

我们订阅了用于更改驱动器状态和设置计数器当前值(冷水和热水)的命令:

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

设备上只有一个按钮-按下我们打开屏幕,绘制计数器,传感器和水龙头的当前读数。 OLED屏幕,如果始终保持打开状态,则快速褪色。

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

这是stm控制器有趣的硬件和软件功能,在参考微粒中,他们将其称为BackupSRAM。光子有一个vbat输出-这不是电池电量或充电。只要这条脚上有电压,就可以在控制器完全断电的情况下保持4k字节SRAM的内容。这样就消除了EEPROM的磨损问题。

在代码中,必须被驱动到该存储器中的变量声明为:retained。我从1.5F的超级电容器实现了硬件。根据数据表,该存储器将在1.6v时死亡,根据我在原型板上的实验,它将在2周的时间内用大约我的电容器。触发传感器时关闭起重机的逻辑是“自主的”,并不取决于与openhab的连接。有一个用于直接驱动控制的三向开关-自动,OFF(打开水龙头),Close(关闭)。

下面的电路图:



Eagle项目以及自定义库可在此处下载

费用是LUT,在轨道上没有缩小。

水浴LUT的最好的朋友


电源单元。我们需要12伏和5伏。在ebay上搜索捐赠者的行:“硬盘驱动器电源适配器5v 12v”,例如this

房屋


它是用PLA塑料在3d打印机(Tarantula Tevo)上打印的。喷嘴0.4毫米,层0.25毫米。盖子是在同一时间,并且是用于安装控制器板的基座。带电源的底座固定在墙上。带有盖子的底座没有用螺钉固定,盖子的拉力足够大(就像祖母的盖子在果酱罐上一样),墙壁的分层结构也起作用。存档中的

3D模型 这是安装在水管上的所有外观。









Openhab


部署在Armbian下的Orange Pi One上。

项目配置
  -    ,  .
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]" }


泄漏传感器需要一个小的变换规则。

配置转换
transform\water_sensor.map
1=dry
0=wet
undefined=undefined

我们形成管理页面:

配置站点地图
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
}
}

以及处理规则:

配置规则
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


要发送消息,我不使用openhab android应用程序的内置功能,也不与它们的云集成。我喜欢Telegram机器人。如何配置和连接机器人可以在Wiki上看到要从gmail邮箱发送信件,如果您具有两重身份验证,则需要为邮件应用程序启用一次性密码,并在openhab配置中设置此密码。

遵守规则。

检查watercount_sensor-控制器仅在更改值或出现错误警报(少于10个周期)时才发送新的泄漏传感器值。我们分析了来文和历史意义,形成了信息性消息。有一个细微差别-尝试不断获取prevoiusItem会给出当前值,但我还没有找到解决方案-我将值设为“ -3秒”,如果有人克服了它,请在注释或PM中写下来。

检查watercount_temp2-检查是否低于37(表示热水已变冷),到达时必须打开流量加热器。

检查watercount_pressure-我们分析当前值和先前值,并以一条消息响应,要求下降到1 atm以下并在其之上增长。

生成发送字符串计数器-从每月24日凌晨1点开始。我们检查这些值现在是否大于上次发送的值。如果较少,请关闭自动发送并生成警报。如果一切正常,我们会记住要发送给《刑法》的计数器的值,那么会将将来的消息正文发送给电报。同时,我们在watercount_sendStr中保存了过去一个月的消耗量。

生成发送字符串计数器 -从cron开始于第24天的23.00。检查是否启用了自动发送(如果已启用)-头盔将计数器发送到运输公司的邮件。事实证明,如果电报中发生错误,我整天有24天的时间,可以解决一些问题,或者只是减少自动发送的时间。

通过评论更新旋转阀-每月关闭/打开水龙头一次以防沸腾的规则。25日凌晨5点-为了不影响洗碗机或洗衣机的工作,但即使到达那里-也不重要,水的重叠大约需要3-4秒。

只有在这里,智能家居才开始……
在单点(openhab)中组合系统,您可以构建一组自治系统无法使用的逻辑。例如:水表增加的事件已经到来-安全系统处于活动状态,前门锁已关闭,洗碗机和洗衣机的能耗低于5瓦-这意味着检测到传感器泄漏。我们组成命令关闭起重机,并在Telegram中向机器人发送消息。但这就像稍后的主题。

Source: https://habr.com/ru/post/zh-CN399459/


All Articles