智能家居:在家庭助理中创建水电图表


我想知道每次我收到水电账单时,我的家人真的在消耗那么多钱吗?好吧,是的,浴室有加热地板和锅炉,但它们不会经常st动。我们似乎还可以节水(尽管我们也喜欢在浴室溅水)。几年前,我已经将水表电表连接到了智能家居,但这还停留在这一点上。直到现在,人们才开始对消费进行分析,而实际上,这篇文章就是关于这一点的。

我最近将Home Assistant用作智能家居系统。原因之一就是有机会组织大量数据的收集,并有可能方便地构建各种图形。

本文中描述的信息不是新的,所有这些使用不同调味料的东西已经在Internet上进行了描述。但是每篇文章通常只描述一个方法或一个方面。我必须比较所有这些方法,然后自己选择最合适的方法。本文仍未提供有关数据收集的全面信息,但这只是我的摘要。因此,欢迎提出建设性的批评和建议。

问题的提法


因此,今天的练习的目标是获得漂亮的水和电消耗图表:

  • 每小时2天
  • 每天2周
  • (可选)每周和每月

这是我们面临的一些困难:

  • , , . .

    , , . home assistant, , mini-graph-card, :

    • ( , )
    • ( , )
  • , home assistant SQLite ( , , MySQL Postgres), . , , json

    {"entity_id": "sensor.water_cold_hourly", "old_state": {"entity_id": "sensor.water_cold_hourly", "state": "3", "attributes": {"source": "sensor.water_meter_cold", "status": "collecting", "last_period": "29", "last_reset": "2020-02-23T21:00:00.022246+02:00", "meter_period": "hourly", "unit_of_measurement": "l", "friendly_name": "water_cold_hourly", "icon": "mdi:counter"}, "last_changed": "2020-02-23T19:05:06.897604+00:00", "last_updated": "2020-02-23T19:05:06.897604+00:00", "context": {"id": "aafc8ca305ba4e49ad4c97f0eddd8893", "parent_id": null, "user_id": null}}, "new_state": {"entity_id": "sensor.water_cold_hourly", "state": "4", "attributes": {"source": "sensor.water_meter_cold", "status": "collecting", "last_period": "29", "last_reset": "2020-02-23T21:00:00.022246+02:00", "meter_period": "hourly", "unit_of_measurement": "l", "friendly_name": "water_cold_hourly", "icon": "mdi:counter"}, "last_changed": "2020-02-23T19:11:11.251545+00:00", "last_updated": "2020-02-23T19:11:11.251545+00:00", "context": {"id": "0de64b8af6f14bb9a419dcf3b200ef56", "parent_id": null, "user_id": null}}}

    ( , ), . SDM220 10-15 , 8. , . .. 100-200 . , ( home assistant raspberry PI), .
  • , . . (RS232/RS485/Modbus/Zigbee) .

    ( ), X - . . , . , , home assistant, , ( home assistant).

1


首先,让我们看看现成的家庭助手。衡量一段时间内的消耗是一项非常需要的功能。当然,它是很久以前以专用组件(utility_meter)的形式实现的。

该组件的本质是它在内部启动变量current_accumulated_value并在指定的时间段(小时/周/月)后将其重置。组件本身监视传入的变量(某种传感器的值),订阅值本身的更改-您仅获得最终结果。在配置文件的仅几行中描述了该内容。

utility_meter:
  water_cold_hour_um:
    source: sensor.water_meter_cold
    cycle: hourly
  water_cold_day_um:
    source: sensor.water_meter_cold
    cycle: daily

这里sensor.water_meter_cold是计数器的当前值(以升为单位),我可以通过mqtt 直接从一块铁中获得。该设计创建了两个新的传感器water_cold_hour_um和water_cold_day_um,它们累积每小时和每天的读数,并在一段时间后将其重置。这是一个半小时的电池图表。

图片

lovelace-UI的每小时和每天的图表代码如下所示:

      - type: history-graph
        title: 'Hourly water consumption using vars'
        hours_to_show: 48
        entities:
          - sensor.water_hour

      - type: history-graph
        title: 'Daily water consumption using vars'
        hours_to_show: 360
        entities:
          - sensor.water_day

实际上,这种方法的问题在于该算法。如前所述,对于每个输入值(当前每升读数),数据库中都会生成1kb的记录。每个公用事业仪表还生成一个新值,该新值也累加到数据库中。如果我想收集每小时/每天/每周/每月的读数,并为几个上升管,再加上一堆电表,这将是很多数据。好吧,更确切地说,没有太多数据,但是由于家庭助理会向数据库中写入大量不必要的信息,因此数据库的大小将突飞猛进。恐怕甚至无法估计每周和每月图表的基础规模。

另外,仅电表不能解决该任务。电度表给出的值图是一个单调递增的函数,每小时重置为0。我们需要一个用户友好的消耗量图表,其间该时期吃了多少公升。标准的历史记录图形组件不知道如何执行此操作,但是外部迷你图形卡组件可以为我们提供帮助。

这是lovelace-UI的卡代码:

      - aggregate_func: max
        entities:
          - color: var(--primary-color)
            entity: sensor.water_cold_hour_um
        group_by: hour
        hours_to_show: 48
        name: "Hourly water consumption aggregated by utility meter"
        points_per_hour: 1
        show:
          graph: bar
        type: 'custom:mini-graph-card'

除了传感器名称,图形类型,颜色(我不喜欢标准橙色)之类的标准设置外,还要注意以下3个设置:

  • group_by:小时-将在小时开始时将列对齐的方式生成图表
  • points_per_hour:每小时1个酒吧
  • , aggregate_func: max — .



不要注意左侧的许多列-如果没有数据,这是组件的标准行为。而且没有数据-就在本文中,我只是在几个小时前才打开公用仪表数据收集(稍后再告诉您我当前的方法)。

在这张照片中,我想表明有时数据显示甚至可以正常工作,并且条形图确实反映了正确的值。但这还不是全部。出于某种原因,从上午11点到12点间隔的选定列出于某种原因显示了19升,尽管在同期的暴牙图表上,我们发现同一传感器的消耗量为62升。虫子或手歪了。但是我不明白为什么右边的数据会中断-那里的消耗是正常的,这在暴牙的时间表上也可以看到。

通常,我没有设法获得这种方法的可信度-图形几乎总是显示出某种异端。

相同的日期传感器代码。

      - aggregate_func: max
        entities:
          - color: var(--primary-color)
            entity: sensor.water_cold_day_um
        group_by: interval
        hours_to_show: 360
        name: "Daily water consumption aggregated by utility meter"
        points_per_hour: 0.0416666666
        show:
          graph: bar
        type: 'custom:mini-graph-card'

请注意,group_by参数设置为interval,而points_per_hour参数控制一切。这是该组件的另一个问题-points_per_hour在一个小时或更短的时间内就可以很好地在图表上工作,但是在很大的间隔内令人讨厌。因此,要在一天内获得一列,我必须输入值1/24 = 0.04166666。我不是在谈论每周和每月图表。

方法2


只是弄清楚了家庭助理,我在这里看到了这个视频:


一个朋友从几种类型的小米商店收集消费数据。他的任务要简单一些-仅显示今天,昨天和一个月的消费价值。不需要时间表。

让我们撇开关于瞬时功率值的手动积分的讨论-我已经写了这种方法的``准确性''。目前尚不清楚他为什么不使用已由同一商店收集的累计消耗值。我认为,整合到铁片中会更好。

从视频中我们想到了手动计算该期间的消费的想法。农民只计算今天和昨天的价值,但我们会走得更远并尝试绘制图表。在我的情况下,所提出的方法的实质如下。

  • ___,
  • ( ) . — , .
  • “” ___ .

所有这些都可以通过家庭助理本人来完成。

您将不得不编写比先前方法更多的代码。首先,让我们获取这些非常“变量”。开箱即用,我们没有“变量”实体,但是您可以使用mqtt经纪人的服务。我们将在那里发送带有retain = true标志的值-这将把值保存在经纪人内部,您甚至可以在家庭助理重启时随时将其取出。我马上进行了小时和小时计数器。

- platform: mqtt
  state_topic: "test/water/hour"
  name: water_hour
  unit_of_measurement: l

- platform: mqtt
  state_topic: "test/water/hour_begin"
  name: water_hour_begin
  unit_of_measurement: l

- platform: mqtt
  state_topic: "test/water/day"
  name: water_day
  unit_of_measurement: l

- platform: mqtt
  state_topic: "test/water/day_begin"
  name: water_day_begin
  unit_of_measurement: l

一切魔术都发生在自动化中,自动化分别在每小时和每个晚上运行。

- id: water_new_hour
  alias: water_new_hour
  initial_state: true
  trigger:
    - platform: time_pattern
      minutes: 0
  action:
    - service: mqtt.publish
      data:
        topic: "test/water/hour"
        payload_template: >
          {{ (states.sensor.water_meter_cold.state|int) - (states.sensor.water_hour_begin.state|int) }}
        retain: true
    - service: mqtt.publish
      data:
        topic: "test/water/hour_begin"
        payload_template: >
          {{ states.sensor.water_meter_cold.state }}
        retain: true

- id: water_new_day
  alias: water_new_day
  initial_state: true
  trigger:
    - platform: time
      at: "00:00:00"
  action:
    - service: mqtt.publish
      data:
        topic: "test/water/day"
        payload_template: >
          {{ (states.sensor.water_meter_cold.state|int) - (states.sensor.water_day_begin.state|int) }}
        retain: true
    - service: mqtt.publish
      data:
        topic: "test/water/day_begin"
        payload_template: >
          {{ states.sensor.water_meter_cold.state }}
        retain: true

两种自动化都执行2个动作:

  • 间隔的值计算为开始值和结束值之间的差
  • 更新下一个间隔的基值

在这种情况下,可以通过通常的历史图表来解决该图形:

      - type: history-graph
        title: 'Hourly water consumption using vars'
        hours_to_show: 48
        entities:
          - sensor.water_hour

      - type: history-graph
        title: 'Daily water consumption using vars'
        hours_to_show: 360
        entities:
          - sensor.water_day

看起来像这样:



原则上,这已经是您所需要的。此方法的优点是每个间隔一次生成一次数据。那些。每小时图表每天只有24个条目。

不幸的是,这仍然不能解决不断增长的基础的普遍问题。如果我需要每月的消费计划,则必须存储至少一年的数据。而且由于家庭助理仅提供一个设置来存储整个数据库的持续时间,因此这意味着系统中的所有数据都必须存储整整一年。例如,一年我消耗200立方米的水,这意味着数据库中有200,000条记录。而且,如果考虑其他传感器,该数字将完全变得不雅。

方法3


幸运的是,聪明人已经通过编写InfluxDB数据库解决了这个问题。该数据库经过专门优化,用于存储基于时间的数据,是存储不同传感器的值的理想选择。该系统还提供了一种类似于SQL的查询语言,使您可以从数据库中选择值,然后以各种方式对其进行汇总。最后,可以在不同的时间存储不同的数据。例如,频繁变化的读数(例如温度或湿度)只能存储几周,而每天的用水量读数则可以存储一年。

除了InfluxDB,聪明人还发明了Grafana,这是一个基于InfluxDB数据的图表系统。Grafana可以绘制不同类型的图,对其进行详细自定义,最重要的是,这些图可以“粘在” lovelace-UI家庭助理上。在这里这里

获取灵感文章详细介绍了将InfluxDB和Grafana安装和连接到家庭助理的过程。我将专注于解决我的特定问题。 因此,首先,让我们开始在influxDB中添加计数器值。一个家庭助理配置的一部分(在此示例中,我不仅可以享受冷水乐趣,还可以享受热水的乐趣):



influxdb:
  host: localhost
  max_retries: 3
  default_measurement: state
  database: homeassistant
  include:
    entities:
      - sensor.water_meter_hot
      - sensor.water_meter_cold

关闭将相同数据保存到家庭助理的内部数据库的操作,以免再次膨胀它:

recorder:
  purge_keep_days: 10
  purge_interval: 1
  exclude:
    entities:
      - sensor.water_meter_hot
      - sensor.water_meter_cold

现在,我们进入InfluxDB控制台并设置数据库。特别是,您需要配置此数据或该数据将存储多长时间。这是由所谓的保留策略-这类似于主数据库内部的数据库,每个内部数据库都有其自己的设置。默认情况下,所有数据都存储在名为autogen的保留策略中,该数据将存储一周。我希望每小时的数据可以存储一个月,每周的数据可以存储一年,而每月的数据绝对不能删除。创建适当的保留政策

CREATE RETENTION POLICY "month" ON "homeassistant" DURATION 30d REPLICATION 1
CREATE RETENTION POLICY "year" ON "homeassistant" DURATION 52w REPLICATION 1
CREATE RETENTION POLICY "infinite" ON "homeassistant" DURATION INF REPLICATION 1

现在,实际上,主要技巧是使用连续查询进行数据聚合。这是一种自动以指​​定的时间间隔启动查询,汇总此查询的数据并将结果添加到新值的机制。让我们看一个示例(出于可读性考虑,我写在一栏中,但实际上我必须在一行中输入此命令)

CREATE CONTINUOUS QUERY cq_water_hourly ON homeassistant 
BEGIN 
  SELECT max(value) AS value 
  INTO homeassistant.month.water_meter_hour 
  FROM homeassistant.autogen.l 
  GROUP BY time(1h), entity_id fill(previous) 
END

该命令:

  • 在homeassantant数据库中创建一个名为cq_water_cold_hourly的连续查询
  • 该请求将每小时执行一次(时间(1h))
  • 该请求将从测量结果中获取所有数据“ a homeassistant.autogen.l(升)”,包括冷热水的读数
  • 汇总的数据将按entity_id分组,这将为冷水和热水创建单独的值。
  • , max(value)
  • homeassistant.month.water_meter_hour, month retention policy . entity_id value

在晚上或没有人在家时,没有水消耗,因此在homeassistant.autogen.l中也没有新条目。为了避免在常规查询中遗漏值,您可以使用fill(上一个)。这将迫使InfluxDB使用过去的小时值。

不幸的是,连续查询有一个特殊性:填充(上一个)技巧不起作用,并且根本不会创建记录。而且,这是已经讨论了一年多的一些不可克服的问题。稍后我们将处理此问题,并在连续查询中填充(上一个)为-它不会干扰。

让我们检查发生了什么(当然,您需要等待几个小时):

> select * from homeassistant.month.water_meter_hour group by entity_id
...
name: water_meter_hour
tags: entity_id=water_meter_cold
time                 value
----                 -----
...
2020-03-08T01:00:00Z 370511
2020-03-08T02:00:00Z 370513
2020-03-08T05:00:00Z 370527
2020-03-08T06:00:00Z 370605
2020-03-08T07:00:00Z 370635
2020-03-08T08:00:00Z 370699
2020-03-08T09:00:00Z 370761
2020-03-08T10:00:00Z 370767
2020-03-08T11:00:00Z 370810
2020-03-08T12:00:00Z 370818
2020-03-08T13:00:00Z 370827
2020-03-08T14:00:00Z 370849
2020-03-08T15:00:00Z 370921

请注意,数据库中的值存储在UTC中,因此在此列表中它们相差3小时-InfluxDB输出中上午7点的值对应于上图中上午10点的值。另外请注意,早上2点到5点之间根本没有记录-这与连续查询的功能相同。

如您所见,聚合值也是一个单调递增的序列,只有记录的频率较低-每小时一次。但这不是问题-我们可以编写另一个查询,该查询将为图表生成正确的数据。

SELECT difference(max(value)) 
FROM homeassistant.month.water_meter_hour 
WHERE entity_id='water_meter_cold' and time >= now() -24h 
GROUP BY time(1h), entity_id 
fill(previous)

我将解密:

  • 从homeassistant.month.water_meter_hour数据库中,我们提取最后一天的实体_id ='water_meter_cold'的数据(时间> =现在()-24h)。
  • 正如我在序列homeassistant.month.water_meter_hour中提到的那样,某些条目可能会丢失。我们将通过使用GROUP BY time(1h)运行查询来重新生成此数据。此时间填充(上一个)将根据需要工作,生成丢失的数据(该函数将使用上一个值)
  • 此请求中最重要的是差异函数,它将计算每小时标记之间的差异。就其本身而言,它不起作用,并且需要聚合函数。使其成为之前使用的max()。

执行的结果看起来像这样

name: water_meter_hour
tags: entity_id=water_meter_cold
time                 difference
----                 ----------
...
2020-03-08T02:00:00Z 2
2020-03-08T03:00:00Z 0
2020-03-08T04:00:00Z 0
2020-03-08T05:00:00Z 14
2020-03-08T06:00:00Z 78
2020-03-08T07:00:00Z 30
2020-03-08T08:00:00Z 64
2020-03-08T09:00:00Z 62
2020-03-08T10:00:00Z 6
2020-03-08T11:00:00Z 43
2020-03-08T12:00:00Z 8
2020-03-08T13:00:00Z 9
2020-03-08T14:00:00Z 22
2020-03-08T15:00:00Z 72

从凌晨2点到5点(UTC)没有消费。不过,由于填充(上一个查询),查询将返回相同的消耗值,并且差值函数将从其自身中减去该值,并在输出处获得0,这实际上是必需的。

剩下的唯一事情就是建立时间表。为此,打开Grafana,打开一些现有的(或创建一个新的)仪表板,创建一个新的面板。图表设置将如下所示。



我将在一张图表上显示冷热水数据。该请求与我上面描述的完全相同。

显示参数设置如下。我将有一个带线的图形,该图形会经过楼梯。我将在下面解释Stack参数。下面还有几个显示选项,但它们并不是那么有趣。



要将收到的时间表添加到家庭助理,您需要:

  • . -
  • , share
  • embed
  • current time range — URL
  • . light
  • URL lovelace-UI

      - type: iframe
        id: graf_water_hourly
        url: "http://192.168.10.200:3000/d-solo/rZARemQWk/water?orgId=1&panelId=2&from=now-2d&to=now&theme=light"

请注意,时间范围(最近2天)是在此处设置的,而不是在仪表板设置中设置的。

该图看起来像这样。最近两天我没有使用过热水,因此只绘制了一张冷水图。



我没有为自己决定最喜欢的时间表,阶梯线或真实杠铃。因此,我将仅以列为单位简单地给出每日消费图表的示例。以与上述相同的方式构建请求。显示参数如下:



该图如下所示:



关于Stack参数。在此图中,一列冷水绘制在一列热水的顶部。总高度对应于该时期内冷热水的总消耗量。

显示的所有图形都是动态的。您可以将鼠标悬停在一个兴趣点上,并查看特定点的详细信息和价值。

不幸的是,两汤匙的焦油不能做到。在条形图上(与带有阶梯线的图相反),条形图的中间不是在一天中,而是在00:00。那些。该列的左半部分代替前一天绘制。因此,周六和周日的图表比蓝色区域稍微靠左绘制。直到我弄清楚如何赢得比赛。

另一个问题是无法按月间隔正确工作。事实是小时/天/周的时长是固定的,但是月份的时长每次都不同。InfluxDB只能定期运行。到目前为止,我的大脑足以设置30天的固定间隔。是的,图表在一年中会浮动一点,并且各列将与月份不完全对应。但是由于这东西只是作为显示设备对我来说很有趣,所以我可以接受。

我看到至少两个解决方案:

  • 按月时间表评分,并限制为每周。一年中有52个每周酒吧看起来不错
  • №2, . . — .



我不知道为什么,但是我正在研究这类图表。他们表明生活如火如荼,一切都在变化。昨天很多,今天还不够,明天还会有其他事情。它仍然需要与家庭合作解决消费问题。但是即使有目前的胃口,付款中只有一个庞大而难以理解的数字已经变成了相当清晰的消费情况。

尽管有近20年的程序员职业生涯,但我实际上并未与数据库相交。因此,外部数据库的安装似乎非常深刻且难以理解。前面提到的文章改变了一切-事实证明,只需单击几下即可拧紧合适的工具,而使用专门的工具,制图的任务将变得更加容易。

在标题中,我提到了用电量。不幸的是,目前我无法提供任何图表。通过Modbus访问时,一个SDM120计数器已失效,而另一个则存在故障。但是,这不会影响本文的主题-图形的构建方式与水相同。

在本文中,我列举了自己尝试的方法。当然,还有其他一些我不知道的组织数据收集和可视化的方法。在评论中告诉我,这对我来说非常有趣。我将为建设性的批评和新想法感到高兴。我希望介绍的材料也能对某人有所帮助。

All Articles