『瀚思彼岸』» 智能家居技术论坛

 找回密码
 立即注册
查看: 188493|回复: 387

[快速入门] 【YAML+Jinja】教程与经验,庆祝论坛人数过万,解除回帖可见

  [复制链接]

26

主题

553

帖子

2728

积分

金牌会员

Rank: 6Rank: 6

积分
2728
金钱
2150
HASS币
100

教程狂人

发表于 2018-4-8 02:45:54 | 显示全部楼层 |阅读模式
本帖最后由 Mirukuteii 于 2018-8-7 09:45 编辑

【YAML+Jinja】基础教程与编写经验,萌新探索之三【8.7更新】

HomeAssistant环境下的YAML与Jinja模板引擎基础教程及编写经验探索

YAML Jinja Home-assistant 基础教程 萌新探索系列

感谢

  • 深深感谢论坛各位大神和坛友们的捧场,你们的支持是我不断前进的动力!
  • 本文将持续跟进与深入,楼主萌新一枚,水平有限,不到之处还请各位大神和坛友批评指正!

更新

  • 4月8日发表本贴。
  • 4月11日更新【Jinja的局限性】,希望得到大神指导。
  • 4月12日使用【MarkDown】格式重新编辑更新帖子,希望大家喜欢。
  • 4月14日新增【代码编写经验梳理】和 Jinja【转义】。
  • 4月16日引用HA官方对YAML的某些说明作为参考,修正概念表述中可能引起歧义的解读,细化YAML中【!include系列】【!secret】【!env_var】等用法。
  • 4月21日新增【缩进的特殊情况】,并以PyYAML的角度,进一步解读了部分YAML概念(主要是纯量类型)。
  • 4月22日更新Jinja【变量滤镜】,新增【变量检验】
  • 4月27日新增Jinja【前置知识】之【HA的状态对象】
  • 4月29日新增Jinja【HA模板扩展专题】之【状态扩展】和【时间扩展】,因楼主连续几日发烧,暂时休息,其它部分下次再更新。
  • 8月7日重新着手更新,准备结合Hass的配置结构和常用组件,重新写一篇基础+进阶版的教程,筹划中。

前言

    作为一名零基础上了论(zei)坛(chuan)的萌新,最近在论坛混了几天,跟着各位大神学习学习再学习,收获很多。然后萌新我也发表几个探索的帖子,感觉自己的努力得到了支持和认可,有了一点点的成就感(或许萌新如我就是这样LOW吧)。
    本想继续发点组件集合之类的帖子,不过自己的时间精力其实也很有限,而且更关键的是水平也很有限,并且最近对YAML配置文件的修改日益频繁,代码更趋冗杂,错误愈至烦乱,调试越加抓狂...
    遂感觉萌新应该沉下心,先把YAML+Jinja语法好好总结一下。同时呢,在论坛里似乎没有找到这样的专门的教程,于是决定在此写个YAML和Jinja模板语言的基础教程,写给自己看,也分享给如我一般的萌新看,大家共同学习,共同提高,在HASSBIAN论坛中不断进步!
    另:如在YAML和Jinja的学习过程中发生疑问,或者有新的好的经验体会填坑分享,可以在我的帖子下面回复或者直接私信我,欢迎一起探讨、试验、论证和总结。

约定

  • HA=“Home Assistant”

一、YAML 基础教程

简介

YAML的意思就是:“我不是标记语言”。
那么标记语言是啥玩意?
这个萌新我知道,HTML(超文本标记语言)就是一种标记语言。
标记语言里有很多种标记,标记用来把文本或者其他内容以某种形式展现出来。
而YAML里面没有各种各样的标记,其本身形式是很利于被人直接阅读和理解的。
所以,我们通常用它来写配置文件,管理各种配置。
YAML的特性用一句话表达如下:
**YAML以数据为中心,使用空白,缩进,分行组织数据,从而使得表示更加简洁易读。**
YAML官网:http://yaml.org,最新版本:1.2
HA的YAML解析器为PyYAML,官网http://pyyaml.org,最新版本3.12

HA官方说明-关于YAML

Home Assistant使用YAML语法进行配置。
YAML可能需要一段时间才能习惯,但在表达复杂配置方面非常强大。

基本规则

  • 大小写敏感
  • 禁止使用tab缩进(推荐整个代码编写中都不要用到tab)
  • 字符串可以不用引号标注

注释方法

用法: 使用#,注释该行中注释符之后的所有内容
解读: 注释区域由YAML文档中任意位置的#开始至该行结束,在HA的YAML配置文件内被注释掉的内容,将不可能被YAML和Jinja解析。
备注: 该方法是YAML文档唯一的注释方法,如果需要多行注释,建议在通用编辑软件中使用快捷键Ctrl+/

字典、列表、缩进

我琢磨,YAML里面最需要注意的是这3种语法:字典、列表、缩进。

① 字典

    一种重要的数据结构,本文所述字典(dictionary)指的是Python等语言中的名称,YAML官方中称为映射(mapping)、JSON中称为对象(object)、数据结构也叫做哈希(hashes)或者散列表等。
    萌新是不是有点晕,没关系,名字千变万化,实质结构并不复杂。

单个成员表示:  键(key): 值(Value)
完整字典表示:  成员1 对齐换行 成员2 ... 或 {成员1, 成员2, ...}
注意:  :或者,要半角英文,后面必须加空格
解读:  以键和值成对出现并组成集合的方式构成的数据结构,比如name: mirukuteii。目的在于为键(对象)构建和配置值(属性),从而对系统进行组件构建、初始化等配置和管理。
代码实例:

api_password:  hassbian  #HA前端登录密码
server_host:   0.0.0.0   #监听所有IPV4端口

代码解读: 这是一个字典,这个字典有两个成员。成员1的键为api_password,值为hassbian;成员2的键为server,值为0.0.0.0。我们通过这种方法来对HA前端登录密码和监听ip地址进行配置。

② 列表

    另一种数据结构,本文所述列表(list)指的是Python等语言中的名称,YAML官方中称为序列(sequence)、JSON中称为数组(array)。

单个成员表示:  - 列表成员
完整列表表示:  成员1 对齐换行 成员2 ... 或 [成员1, 成员2,...]
注意:  -或者,要半角英文,后面必须加空格
解读:  以若干成员(元素)组成一个列表的数据结构,比如[dog, cat]
代码实例:

monitored_conditions:   #网速检测项目
  - ping                #ping响应速度
  - download            #下载速度
  - upload              #上传速度

代码解读: 这是一个字典(或字典成员),键为monitored_conditions,值为一个列表,其成员分别为ping、download、upload。我们通过这种方式配置所需要的网速测试项目分别为ping、download、upload。

③ 缩进

    并非一种数据结构,而是为辅助构建复合嵌套的数据结构而存在。

表示:  两个空格 或者 利用{}[]的包含与被包含关系来确定
注意:  关键要对齐,不能用tab代替空格,小心你的代码编辑器自动用tab缩进!
解读:  表示和管理数据间层次关系(嵌套)的方法。
代码实例:

http:
  api_password:  hassbian  #HA前端登录密码
  server_host:   0.0.0.0   #监听所有IPV4端口

也可以写成:

http: {api_password: hassbian, Server_host: 0.0.0.0}

代码解读: 这是1个字典,其键为http,其值为另1个字典(包含2个字典成员,键分别为api_password和Server_host)。空格缩进逐级表示出层次关系(缩进后的字典从属于不缩进的字典,相同缩进的字典项是组成完整字典的平等的成员)。我们通过这种方法,配置组件http下的两个子属性,api密码和ip地址监听列表,其值分别为hassbian与0.0.0.0。
特殊情况:
4个空格缩进的情形:当字典嵌套字典又嵌套在列表中时,比如

- 字典键1:
    字典键2: "字典值"

代码解读: 因为实际嵌套了2层次,所以字典键2这一行必须缩进4个空格,而不是2个。若只缩进两个,侧字典键1与字典键2位并列的字典成员关系。

理解了以上3个基本语法,剩下的不过是复合嵌套罢了。

④ 复合嵌套

其实缩进本身就是管理复合嵌套关系的一种语法,但是这里我将再举个复杂点的例子。我在上个帖子中发表了一段关于网络测速PKG的YAML代码,原理是利用系统自带组件sensor.speedtest进行网络测速。我就顺便以此为例简单介绍复合嵌套。
代码实例:

sensor:
  - platform: speedtest  
    server_id: 5316      #南京电信测试节点
    monitored_conditions:
      - ping             #sensor.speedtest.ping
      - download
      - upload
    hour:
      - 2
      - 14
    minute: 22
  - platform: speedtest
    server_id: 4665      #上海移动测试节点
    monitored_conditions:
      - ping             #sensor.speedtest.ping_2

代码解读: 构建1个键为sensor的字典,其值由列表(含2个成员)构成。列表成员1为1个字典,有5个字典成员,其中的2个(键分别为monitored_conditions、hour)的值又分别由列表构成;列表成员2也是1个字典,有3个字典成员,其中最后的字典成员的值又由列表(成员只有1个)构成。配置组件类型sensor下的子组件speedtest(同时开2个),第1个每天2时22分和14时22分连接南京电信测试节点测试ping、下载、上传3个项目;第2个按照默认的时间(每小时)连接上海移动测试节点测试ping值。

⑤ 经验总结

    在配置HA中总结的YAML经验,拿出来分享一下,不一定准确,希望群友及时批评指正。

体会1:HA的配置文件中,重点和基础是字典结构

就是: 属性: 值
解读: 作为一个配置文件,这是一种核心的配置手段。就像注册表与ini文件一样,必须建立项目与数据的关系。

HA官方说明-映射(字典)结构

YAML语法的基础是块集合和包含键值对的映射。 集合中的每个项目都以一个 - 开始,而映射具有格式key:value。 如果指定重复键,则使用键的最后一个值。 这有点类似于哈希表或者更具体的Python中的字典,它们也可以进行嵌套。

体会2:在此基础上,以缩进(或者{}[]的包含关系)管理配置的层次关系

比如:对一个物联网交互组件的简单配置,结构通常如下

组件域名:
  平台:XXX 
  配置项1: XXX
  配置项2: XXX

扩展解读:在物联网交互组件中,组件提供某些功能的核心逻辑。平台可以连接到特定的软件或硬件平台。一个物联网交互组件下面往往包含多个平台。比如灯组件,有hue平台、lifx平台等。平台的配置项与平台并列。

HA官方说明-缩进结构

请注意,缩进是使用YAML指定关系的重要部分。缩进的东西嵌套在“更高一层”的东西里面。如果您不使用固定宽度字体的编辑器,那么获得正确的缩进可能会非常棘手。 制表符不允许用于缩进。 我们约定使用2个空格作为每个缩进的级别。

体会3:在此基础上,以列表扩展取值范围

比如:

组件1:
  实体1: 
    属性1: XXX
    属性2:
      - XXX
      - XXX
  实体2: 
    属性3:
      - XXX
      - XXX

发现有的新坛友都会问如何在YAML中添加多个相同的设备,本文这里就是答案,自发布之日起就存在在这里了,楼主我虽然是萌新,但这个教程也不是随便写的,很多例子都有经验在里面,请注意看看下面的例子!

体会4:注意列表不仅可以用在值上,还可以用来对组件内部的不同平台或实体进行分组

比如:

组件1:
  - 实体1的属性1: XXX 
    实体1的属性2: XXX
    实体1的属性3:
      - XXX
      - XXX
  - 实体2的属性1: XXX 
    实体2的属性4: XXX
    实体2的属性5:
      - XXX
      - XXX

所以,当某个组件下面出现多胞胎的子组件或者子属性组合时,应当用列表分开。

实际中的案例:

配置2个小米网关,用列表方法将mac与key属性组合打包并与其它属性分开。

xiaomi_aqara:
  gateways:
  - mac: !secret xiaomi_gateway_mac1
    key: !secret xiaomi_gateway_key1
  - mac: !secret xiaomi_gateway_mac2
    key: !secret xiaomi_gateway_key2

纯量

纯量的意义

    YAML最后一种也是最基本的数据结构,YAML官方中称为纯量(scalars),也称为原子量。

解读:  纯粹抽象概念,即相对于以上各类字典、列表以及复合嵌套等复杂结构来说,不可再分割的最基本的节点(node)。YAML官方说法凡是能够用一系列的零到若干个可打印的unicode字符表示的数据,都可以作为纯量。
例子:  age: 33中的age33都是纯量!

① 整数型纯量(intergers)

  • 标准写法:   12345
  • 十 进 制:    + 12345
  • 八 进 制:    0o14
  • 十六进制:   0xc
    解读:只有数字和下划线构成的值,将被YAML解析为整数,下划线将自动忽略!

② 浮点数(floating point)

  • 标准写法:    1.23015e + 3
  • 指数写法:    12.3015e + 02
  • 固定小数点: 1230.15
  • 负无穷大:     -.inf
  • 无法以数字衡量:  .NAN

③ 布尔值、NULL

  • NULL:  null或者~
  • 布尔值:    True, False
    解读:true, false; yes, no; on, off同样有效(PyYAML将解析为True或False)

④ 时间日期

  • 标准写法:2001-12-15T02:59:43.1Z
  • ISO8601:2001-12-14t02:59:43.10-05:00
  • 空格写法:2001-12-14 21:59:43.10 -5
  • 日期写法:2002-12-14
    解读:凡是这样写的值,会被YAML自动解析为Pythondatatime格式

⑤ 字符串

字符串:'123456'
解读:HA配置文件中最常见也最重要的纯量类型,在YAML中通常的字符不加引号也会自动认为是字符串,引号可以用'也可以用",注意相互嵌套关系。使用"表示字符串时,特殊字符可以通过倒斜线(\x* 以及 \u***)来进行转义。

特殊符号和操作

①强制转换数据类型

用法: !! XXX XXX表示强制转换的类型
例如: notdata: !! str 2018-04-09相当于notdate: '2018-04-09'

②多行字符串折叠换行

用法: >
解读: 表示>下面的多行字符串行之间不保留换行符\n
实例:

action:
  - service: notify.weixin_sysnotify
    data_template:
      title: "设备联网状态变更"
      message: >
        设备:{{ trigger.event.data.new_state.attributes.friendly_name }}
        {%- if trigger.event.data.new_state.state == '离线' %}
        已断开家庭网络
        {%- elif trigger.event.data.new_state.state == '在线' %}
        已接入家庭网络
        {% endif -%}

收到的微信提醒信息为:
设备:XXXXX已断开(或接入)家庭网络

③多行字符串保留换行

用法: |
解读: 表示|下面的多行字符串行之间保留换行符\n
实例:

action:
  - service: notify.weixin_sysnotify
    data_template:
      title: "设备联网状态变更"
      message: |
        设备:{{ trigger.event.data.new_state.attributes.friendly_name }}
        {%- if trigger.event.data.new_state.state == '离线' %}
        已断开家庭网络
        {%- elif trigger.event.data.new_state.state == '在线' %}
        已接入家庭网络
        {% endif -%}

收到的微信提醒信息为:
设备:XXXXX
已断开(接入)家庭网络

④字符串保留结尾换行符

用法: + 一般用在|之后
解读: 表字符串行之后的换行符\n被保留
实例:

value: |+
  hahaha
#
#

解读: value = hahaha\n\n

⑤字符串不保留结尾换行符

用法: - 一般用在|之后
解读: 表字符串行之后的换行符\n被保留
实例:

value: |-
  hahaha

解读: value = hahaha

HA中的特殊用法

!XXXXX

暂时理解为HA内部定义的标签,用于替换内容
例如:!include系列、!secret!env_var

①!include系列

  • !include:调用单个外部文件加入指定位置。
    下例解读:调用外部文件lights.yaml中的内容作为lights组件的值。
    lights: !include lights.yaml
  • !include_dir_list:调用外部目录下的每个文件,并将其内容作为单个列表的成员,最后将整个列表加入指定位置。
    下例解读:调用外部目录lights中的每个文件,并将其内容作为列表的一个成员,最后将整个列表作为组件lights的配置属性(值)。注意:文件名不会被涉及,每个文件的内容是列表成员。
    lights: !include_dir_list lights
  • !include_dir_named:调用外部目录下的每个文件,并将其文件名和文件内容分别作为单个字典成员的键和值,最后将整个字典加入指定位置。
    下例解读:调用外部目录lights中的每个文件,并将其文件名和文件内容分别作为单个字典成员的键和值,最后将整个字典作为组件lights的配置属性(值)。注意:文件名会被作为字典成员的键,每个文件的内容则作为字典成员的值。
    lights: !include_dir_named lights
  • !include_dir_merge_list:调用外部目录下的所有文件作为列表加入指定位置。
    下例解读:调用外部目录lights中的每个文件,将其内容直接合并,最后以一个大列表作为组件lights的配置属性(值)。
    lights: !include_dir_merge_list lights
  • !include_dir_merge_named:调用外部目录下的所有文件作为字典加入指定位置。
    下例解读:调用外部目录lights中的每个文件,将其内容直接合并,最后以一个大字典作为组件lights的配置属性(值)。
    lights: !include_dir_merge_named lights

注意:不管是哪种涉及调用整个外部目录下文件的方法,该目录下的所有文件都将被递归调用,包括任何子目录下的文件。

②!secret 调用secret.yaml文件中的私密变量

用法举例:

api_password: !secret APIPWD

解读:使用secret.yaml文件中的字典项APIPWD: hassbian作为api_password的值,别忘了在secret.yaml中包含如下代码:

APIPWD: hassbian

注意:如果你在某个yaml文件中使用!secret,那么HA将首先查找该yaml文件同目录下的secret.yaml并进行调用,若无法调用成功,则会查找上一级目录下的secret.yaml并进行调用,直至查找到HA配置文件的根目录(configuration.yaml所在的目录)。你可以通过这种方法来设置该私密变量的有效范围。

③!env_var 调用系统环境变量

用法举例:

api_password: !env_var HA_PWD hassbian

解读:使用系统环境变量HA_PWD作为api_password的值,若不存在该环境变量,则使用hassbian作为密码。设置环境变量有诸多方法,不在本文探讨范围。

/XX

在自动化触发组件【automation.trigger】中,属性hours、minutes、sencond的值可以为/XX,表示每隔XX个时间(小时、分钟、秒)触发该自动化

  • 目前想到这么多,待补充

待补充项

YAML教程暂时就写到这里,不定期更新和深入

二、Jinja 基础教程

简介

    如果看到这里你还不知道Jinja是什么,没关系,只要在论坛里混过那么几个日夜,拜读过大神那么几篇帖子,配置过那么几段代码,那么你对下面这种代码肯定不陌生:
data_template:
  message: >
    {% if is_state('device_tracker.paulus', 'home') %}
      Ha, Paulus is home!
    {% else %}
      Paulus is at {{ states('device_tracker.paulus') }}.
    {% endif %}
    以上代码显然有别于前面教程里的YAML的体系,这就是Jinja,基于Python的一种模板(Template)引擎,它让本来死板的YAML拥有更大的灵活性和效率性。
    官网:http://jinja.pocoo.org/     最新版本:2.10

HA官方说明-关于模板

*模板是Home Assistant的一个重要特性。
模板允许用户对进出系统的信息进行控制。
比如:

  • 格式化输出一段消息,例如notify组件、alexa组件等。
  • 对信息源传入的原始数据进行处理,例如MQTT组件、REST传感器组件、命令行传感器组件等。
  • 自动化模板。*

HA官方说明-关于Jinja2

Home Assistant中的模板采用Jinja2模板引擎。这意味着我们正在使用它们的语法,同时在渲染过程中我们也为模板创造了一些自定义的HA变量。

前置知识1 HA的基本架构(非必需)

在开始Jinja的学习前,建议大家先了解一下HA的基本架构:
【HA官方开发指南】【自译】第一篇 HA架构和组件

前置知识2 HA的状态对象(非必需)

然后,再了解下HA中的状态对象(State Objects)
首先,我按照官网说法简单介绍如下:
1.在HA中所有的设备都以实体(Entity,重要概念请记住!)表示。
2.实体将其当前状态写入状态机以供其他实体/模板/前端访问。
3.我们通过各种状态来“看”实体,这些状态就是实体当前的代表。
4.所以HA中用实体的状态对象来代表实体。
简单分,可以用以下的层次来说明:
设备->实体->状态对象->状态字段->属性扩展字段

状态对象(State Objects)通常包含以下字段:

  • state        :以字符串形式,表示当前状态
  • entity_id    :实体代号,由domian.object_id组成
  • domain       :实体的域名
  • object_id    :实体的对象代号
  • name         :实体名,必须为英文,在HA后台中使用的实体名
  • last_updated :状态写入状态机的时间。请注意,编写完全相同的状态(包括属性)不会导致更新此字段。
  • last_changed :状态改变的时间,当只有属性改变时不会导致更新此字段
  • attributes   :以字典形式,表示当前状态的额外属性

    attributes属性字段(本身就是个字典)中通常包含以下字段:

  • friendly_name:实体名昵称,可以用中文,在HA前端看到的实体名
  • icon:实体图标,在HA前端看到的实体图标
  • hidden:实体是否在HA前端可见
  • entity_picture:实体图片,用来在HA前端中取代图标
  • assumed_state        :实体状态是否为假定状态。(即:HA无法从实体获取状态反馈信息,只能通过最后的命令来假设当前实体的状态。)
  • unit_of_measurement:实体状态的度量单位。

要是看了以上内容还比较懵逼,下面就看我举几个例子吧。
下面我以小米网关自带小彩灯为例。
这个设备在HA中表示为1个实体
实体表示为:
light.gateway_light_XXXXXXXXXXXX(XXX代表网关MAC地址,网关不同,MAC地址当然也不一样,这种命名方法来自于xiaomi_aqara组件)
这个实体的各种状态,以状态对象来表示。
实体的状态对象表示为:
states.light.gateway_light_XXXXXXXXXXXX
这个实体的状态对象的状态字段可以这样写(在下面的小节中,你会发现这不是最好的写法):
实体的状态对象的状态字段对应值表示为:
states.light.gateway_light_XXXXXXXXXXXX.state
如果我们要读取这个实体的状态对象的属性字段中的friendly_name字段对应值,可以这么写(在下面的小节中,你会发现这不是最好的写法)
这个实体的状态对象的属性字段的昵称字段对应值表示为:
states.light.gateway_light_XXXXXXXXXXXX.attributes.friendly_name

所以,我们要想得知这个小米网关小彩灯的状态(onoff)时,
只需要读取states.light.gateway_light_XXXXXXXXXXXX.state即可
我们想要得知这个小米网关小彩灯在前端显示的名称(一个字符串)时,
只需要读取states.light.gateway_light_XXXXXXXXXXXX.attributes.friendly_name

更多细节和最佳使用姿势请参看后面的HA模板扩展专题一节
看到这里,我想总明白状态对象了吧。
在Jinja中,你会经常和状态对象打交道的。

前置知识3 HA的事件(非必需)

本项主要涉及到在自动化中编写Jinja代码所需要的前置知识。
有空再写吧

标记符

  • {% ... %} 块标记符,一般用来执行语句
  • {{  ...  }}  变量标记符,一般用来输出变量
  • {# ... #}   注释标记符,一般用来调试代码,使某段代码无效

变量

除了普通的字符串变量,Jinja2还支持列表、字典和对象。

变量赋值

变量赋值通常用 set ,并可以为多个变量赋值:

{% set name = 'mirukuteii' %}
{% set solar_angle = states.sun.sun.attributes.elevation %}

局部变量:使用with语句来创建一个内部的作用域,这样创建的变量只在with代码块中才有效。

{% with temp = 42 %}
{{ temp }}
{% endwith %}

变量输出

{{ temp }}
同时,我们可以通过.[]访问变量的属性:

{{ states.sun.sun.attributes.elevation }}
{{ states.sun.sun.attributes["elevation"]) }}

以上两种方式几乎没有差别,但当属性值内含有空格时,请使用后者。

算术运算符

算术运算符 意义 解读
+ 加法 {{ 1 + 1 }} 返回 2
- 减法 {{ 1 - 1 }} 返回 0
* 乘法 {{ 2 * 3 }} 返回 6
/ 除法 {{ 1 / 2 }} 返回 0.5
// 整除 {{ 5 // 3 }} 返回 1
% 整除 {{ 5 % 3 }} 返回 2
** 乘方 {{ 2 ** 3 }} 返回 8

比较运算符

比较运算符 意义 解读
== 等于 {{ 1 == 1 }} 返回 true
!= 不等于 {{ 1 != 1 }} 返回 false
> 大于 {{ 2 >  3 }} 返回 false
>= 大于等于 {{ 6 >= 6 }} 返回 true
< 小于 {{ 5 <  6 }} 返回 true
<= 小于等于 {{ 5 <= 6 }} 返回 true

逻辑运算符

逻辑运算符 意义 解读
and {{true and false}} 返回 true
or {{true or false}} 返回 false
not {{not false}} 返回 true

其它操作符

其他操作符 意义 解读
in 在...之内(是否包含) {{1 in [1,2,3]}} 返回 true
is 是(是否同一对象) {{a is a}} 返回 true
| 滤镜
()
~ 拼接字符串
./[] 获取属性

控制结构

if 表达式

{{ 做某事  if  满足条件  else  做另一件事  }}
实例:

{{ '在线' if is_state('device_tracker.iphone', 'home') else '离线' }}

if 语句

if后直接跟变量,可以测试该变量是否未定义,为nullfalse
if语句关键词:ifelifelseendif
用法举例:

{%- if states.sensor.moon -%}
  {%- if states.sensor.moon.state == 'New moon'           -%} 新月
  {%- elif states.sensor.moon.state == 'Waxing crescent'  -%} 娥眉月
  {%- elif states.sensor.moon.state == 'First quarter'    -%} 上弦月
  {%- elif states.sensor.moon.state == 'Waxing gibbous'   -%} 盈凸月
  {%- elif states.sensor.moon.state == 'Full moon'        -%} 满月
  {%- elif states.sensor.moon.state == 'Waning gibbous'   -%} 亏凸月
  {%- elif states.sensor.moon.state == 'Last quarter'     -%} 下弦月
  {%- elif states.sensor.moon.state == 'Waning crescent'  -%} 残月
  {%- else -%} 未知月相
  {%- endif -%}
{%- endif -%}

由于Jinja中没有选择语句,所以通常用elif方法代替选择。

for 语句

待编辑

变量滤镜

滤镜(过滤器)用来修正变量,使用一个竖线和变量相隔。
在HA的配置文件中灵活使用滤镜将使得你的代码简洁高效。
Jinja内置过滤器有很多,下面举几个常用的过滤器:

  • int:将变量转换为整数
  • float:将变量转换为浮点数
  • abs:取变量的绝对值
  • round('精度'):按照精度(保留小数点后X位数)进行四舍五入。
  • join('分隔符'):以分隔符组合元素为字符串,也可不设定分隔符直接组合。
  • length:返回该列表或字典变量的成员数。
  • replace("a","b"):把字符串中的a替换为b。
  • default('默认值'):若变量为定义,则变量值修正为默认值,缩写:d
  • first:返回该列表变量的第一个成员
  • format:应用Python字符串格式化
  • 待编辑

例子:

value_template: '{{ value | multiply(0.001) | round(1) }}'

说明:对变量进行了两次过滤,先乘0.001(就是除以1000),再保留小数点后1位进行四舍五入。

扩展说明:
细心的朋友可能注意到了,为什么上述例子中不直接将变量除以1000而是要弯弯绕绕用乘法滤镜乘以0.001,这看上去有点反人类不是么。原因很简单:在{{ ... }}中通过滤镜操作变量更加方便简洁,我们往往无法对变量进行直接运算。
这么写是不行的

value_template: '{{ value / 1000 |round(1) }}'

要写成这样才行

value_template: '{{ (value|int) / 1000 |round(1) }}'

变量检验

除了用滤镜修正变量,我们还用检查器检验变量。
检验通常在if语句块中进行,方法为在变量后加上一个 is 以及检查器的名字。
Jinja内置检查器有很多,下面举几个常用的检查器:

  • defined:检验变量是否定义
  • lower:检验变量是否为小写
  • mapping:检验变量是否为字典
  • sequence:检验变量是否为列表
  • number:检验变量是否为数字
  • even:检验变量是否为偶数
  • divisibleby('除数'):检验变量是否能被除数整除

例子:

{% if name is defined %}

说明:测试变量name是否存在,存在则返回true。

空格控制

使用-来控制空格和换行,一般必须紧邻%
举例说明
例1代码:

a
    {% if True %}
         b
    {% endif %}
c

例2代码:

a
    {% if True -%}
         b
    {%- endif %}
c

例3代码:

a
    {%- if True -%}
         b
    {%- endif -%}
c

例1输出结果:

a

         b

c

例2输出结果:

a
         b
c

例3输出结果:

abc

转义控制

        就是不解析Jinja代码,直接输出其中的内容。

用法:使用{% raw %}{% endraw %}将转义内容包括进来

{% raw %}
{{states(sun.sun)}}
{% endraw %}

结果为:

{{states(sun.sun)}}

HA模板扩展专题

状态扩展

  • states包含了你的HA中所有的状态对象(按实体id字母顺序排列)
  • states.domain包含了该域名(实体类别)下所有的状态对象(按实体id字母顺序排列)
  • states.domain包含了该域名(实体类别)下所有的状态对象(按实体id字母顺序排列)
  • states.sensor.temperature返回实体sensor.temperature的状态对象
  • states('device_tracker.paulus')返回实体device_tracker.paulus的状态对象,若该实体不存在,则返回unknown
  • is_state('device_tracker.paulus', 'home')将测试给定的实体是否处于指定的状态。
  • state_attr('device_tracker.paulus', 'battery')返回实体的属性值,若不存存在该属性,则什么也不返回。
  • is_state_attr('device_tracker.paulus', 'battery', 40)将测试给定的实体的属性值是否处于指定的状态。

从以上部分内容不难发现,HA对于实体的stateattributes更多关注和扩展,使得我们的访问更加方便。
再以小米网关小彩灯为例:
前置知识中灯的状态写法为:
states.light.gateway_light_XXXXXXXXXXXX.state
更强壮的写法应为:
states('light.gateway_light_XXXXXXXXXXXX')
两个的区别就在于后者的容错性更高,实体不存在时不会报错。
其它扩展用法也是同理,不啰嗦了。

时间扩展

  • now()返回当前本地时间
  • utcnow()返回当前UTC时间
    这里注意,以上两个扩展包含特定的值可供访问:
    • *.second*.minute*.hour*.day*.month*.year*.weekday()*.isoweekday()
  • as_timestamp()将日期时间对象或字符串转换为UNIX时间戳
  • strptime(string, format)根据指定的格式把一个时间字符串解析为时间元组,用法和python中相同。
  • 滤镜:|timestamp_local 将UNIX时间戳转换为本地时间
  • 滤镜:|timestamp_utc 将UNIX时间戳转换为本地时间
  • 滤镜:|timestamp_custom(format_string, local_boolean)将UNIX时间戳转换为自定义格式,默认使用本地时间戳

数学扩展

待更新

正则扩展

待更新

其它扩展

待更新

Jinja的局限性

1.在YAML中的局限性

①Jinja是独立于YAML设计的,它作为一种模板引擎服务于各种环境。
②所有的Jinja代码必然以{打头。({{{%
③YAML中字典结构可以以{ ... }(流式映射的方式)来构建。
④要使得jinja代码正常工作,只能把{打头的一整段代码放置于字符串结构中。
⑤所谓字符串结构,可以'"引用,也可以是多行字符串形式。
⑥不把{打头的Jinja代码放在字符串结构中,YAML就会在解析时把它当做字典处理,从而发生错误,比如直接解析{%时报错:%不可以作为字典的键
结论一:在YAML下,Jinja代码要正常运行,必须包含在字符串结构中。

2.在HA环境下的局限性

HA环境下,当我们使用模板(template),并构建一个模板实体的配置项与属性的过程中,不允许配置项为没有定义过的配置项(字典的键),从而无法使用Jinja代码来编写配置项。
例如:

device_tracker.iphone: {'friendly_name': 'iphone', '{{"entity_picture" if is_state("device_tracker.iphone", "home") else "icon"}}': '{{"local/pic/me.jpg" if is_state("device_tracker.iphone5", "home") else "mdi:cellphone-iphone"}}'}

解读:上述代码目的在于跟踪手机,并在手机在家时,设置实体的图标为机主的照片,否则为手机的图标。YAML和Jinja语法上并没有任何错误,但是无法通过HA配置。
报错信息:
{{"entity_picture" if is_state("device_tracker.iphone5", "home") else "icon"}}不是一个正确的配置选项,无法通过配置。
所以,只有entity_picture或者icon这种特定的字符串才能作为一个正确的配置选项。本例的解决办法是使用Cunstom UI中的JavaScript脚本。在这里感谢@yoyosuka老哥的交流指导。
结论二:在HA下,Jinja代码段无法作为字典的键,即HA的组件或属性。
事实上,HA官网文档中所有的Jinja例子都是用来输出字典成员的值的。

3.经验总结:

在HA下的YAML配置文件中,一段Jinja代码,只能在字符串中起作用,并只能在字典的值中发生作用。无法影响到值外的键,也无法构建值内创建结构。

三、代码编写经验梳理

  • 找一个顺手好用的代码编辑器,会让你事半功倍。
  • 涉及语法的符号请用英文半角字符。
  • 注意YAML文件的编码格式,中文请使用UTF-8
  • 时刻注意组件、平台、实体等的名称是否与其对应的名称一致,比如大小写,下划线、空格等容易被忽视之处。
  • 用states()和is_state()代替states.domain.entityid.state的写法.
  • friendly_name、单行模板代码等,用"引用
  • 模板代码外面用",里面用', 比如message: "{% if trigger.to_state.name == 'Dale\'s Bedroom' %}Someone's in your base, killing your noobs!{% else %}It's just another door.{% endif %}"
  • 多行模板代码外面不需要用引号。

    持续更新中



评分

参与人数 21金钱 +169 收起 理由
cym9007 + 5
juneberry + 5 赠人玫瑰,手留余香!
Perry_H + 2
sorrypqa + 8 大神666!
adieu + 2 感谢楼主分享!
hhzx234 + 5 大神666!
eric + 8 谢谢分享!
Tozy + 10 谢谢分享!
plutosherry + 7 膜拜大神!
freexx + 7
debitus + 7 谢谢分享!
lidicn + 20 我来瀚思就为看你!
jyz_0501 + 7 分享是一种美德!
miracle + 10 上学的时候笔记一定写的很好。.
paochu_2007 + 7 专门注册账号来顶你!
qq200800704 + 7 现在才知道原来yaml是这样的,谢谢楼主大大.
心无痕星有迹 + 7 赠人玫瑰,手留余香!
yoyosuka + 5 感谢指路,感谢。
+ 20 在下对你的景仰犹如滔滔长江之水,连绵不绝.
neroxps + 10

查看全部评分

回复

使用道具 举报

75

主题

1976

帖子

8199

积分

元老级技术达人

积分
8199
金钱
6173
HASS币
430

活跃会员教程狂人

发表于 2018-4-8 08:17:42 | 显示全部楼层
这种帖子对我这种不懂编程的小白来说,技术含量很高啊,楼主太谦虚了,谢谢分享。
所有过往,皆为序章。
回复

使用道具 举报

123

主题

4664

帖子

1万

积分

管理员

囧死

Rank: 9Rank: 9Rank: 9

积分
16442
金钱
11693
HASS币
45
发表于 2018-4-8 08:32:06 | 显示全部楼层
强烈感谢楼主的巨大贡献!
回复

使用道具 举报

11

主题

337

帖子

1390

积分

金牌会员

iLee

Rank: 6Rank: 6

积分
1390
金钱
1052
HASS币
0
发表于 2018-4-8 08:54:06 | 显示全部楼层
很到位
群名叫iLee
GitHub叫ileez
Docker Hub叫zlee

这么多名字都是被逼的。。。
回复

使用道具 举报

12

主题

545

帖子

2041

积分

金牌会员

Rank: 6Rank: 6

积分
2041
金钱
1496
HASS币
0
发表于 2018-4-8 10:23:47 来自手机 | 显示全部楼层
写得非常好 666 ,我懒得去找资料学习了。看楼主的介绍就可以了解了,期待楼主下回发个分析查看日志找问题的帖子
回复

使用道具 举报

5

主题

115

帖子

624

积分

高级会员

Rank: 4

积分
624
金钱
509
HASS币
0
发表于 2018-4-8 11:59:13 | 显示全部楼层
总结的很好,适合我们小白
回复

使用道具 举报

30

主题

997

帖子

4153

积分

论坛元老

Rank: 8Rank: 8

积分
4153
金钱
3151
HASS币
0

活跃会员

发表于 2018-4-8 13:53:58 | 显示全部楼层
这种帖子太难得了,必须顶
回复

使用道具 举报

1

主题

99

帖子

405

积分

中级会员

Rank: 3Rank: 3

积分
405
金钱
306
HASS币
0
发表于 2018-4-8 20:08:47 | 显示全部楼层
萌新老师好!  
希望多多出教程!!!!
回复

使用道具 举报

9

主题

787

帖子

3862

积分

论坛元老

Rank: 8Rank: 8

积分
3862
金钱
3075
HASS币
87
发表于 2018-4-8 21:03:20 | 显示全部楼层
这个基础太赞了
回复

使用道具 举报

1

主题

91

帖子

956

积分

高级会员

Rank: 4

积分
956
金钱
865
HASS币
0
发表于 2018-4-8 23:16:24 | 显示全部楼层
make一下
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-12-22 11:18 , Processed in 0.120266 second(s), 34 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表