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

 找回密码
 立即注册
查看: 2506|回复: 69

[基础教程] hacs-Pyscript基础教程

  [复制链接]

4

主题

60

帖子

516

积分

论坛技术达人

积分
516
金钱
446
HASS币
40
发表于 2024-3-19 16:27:45 | 显示全部楼层 |阅读模式
本帖最后由 wz1st 于 2024-3-19 16:41 编辑

简单介绍

用pyscript可以在hass中写python脚本,实现一些自动化功能,相比于homeassistant的自动化,pyscript更灵活,可以实现一些更复杂的自动化功能,pyscript执行速度相较于homeassistant的自动化更快。
更重要的是。。。可以调试  知道问题出在哪
相比NR采用轮询的方式获取状态,pyscript采用事件驱动的方式获取状态,可以减少资源消耗,能更及时对实体状态改变做出响应。

前置技能

  • 有一丢丢的python基础,至少能看懂代码、会debug

安装

  • hacs中找pyscript,集成中添加pyscript,安装完成后在/config/pyscript里新建xxx.py就可以秃头开发了
  • hassio加载项中安装vscode(可选,方便调试)

功能介绍

实体操作

传感器、选择器、输入框

  • 获取实体状态:
    #举个栗子:获取小爱收到的消息
    state = state.get("sensor.xiaomi_lx06_e7c0_conversation")# 只有收到的信息:打开窗帘
    state = state.getattr('sensor.xiaomi_lx06_e7c0_conversation')#得到一个python字典,包括收到的信息、TTS信息、发送的时间等
  • 修改实体状态:
    #举个栗子:设置温度传感器值,修改为摄氏度
    state.set("sensor.ikuai_cpu_temperature", 25.5, attributes={"unit_of_measurement": "°C"})
    state.setattr("sensor.ikuai_cpu_temperature", friendly_name="CPU温度传感器", unit_of_measurement="°C")

    温度传感器的温度值只能使用state.get获取,state.getattr获取到的结果是{'querytime': '2024-03-19 13:38:52', 'unit_of_measurement': '°C', 'device_class': 'temperature', 'icon': 'mdi:thermometer', 'friendly_name': 'iKuai CPU_temperature'}并没有温度值
    当然,也无法用state.setattr去设置温度值,因为state.setattr只能修改实体状态的属性,而温度值是实体的值,无法修改。

开关、按钮、灯、空调等

开关、按钮、灯、空调等实体,都可以通过state.get和state.getattr获取状态,但是无法通过set来设置状态。只能使用服务去开启。

服务

  • 调用服务:
    #举个栗子:开灯
    light.turn_on(entity_id="light.mijia_group3_0112_light", brightness=255, kelvin=4000) #开灯,最高亮度,色温4000k
    service.call("light", "turn_on", entity_id="light.mijia_group3_0112_light", kelvin=4000) #同上
  • 注册服务:

    通过python新建一个服务,执行自己想要的结果,在这个服务中可以使用state.getstate.set等操作;
    然后通过@service函数注解注册服务到homeassistant中,这样就可以通过页面、HA自动化甚至service.call来调用这个服务了。

举个栗子:猫眼视频保存,参考我的另一个帖子: 米家猫眼视频本地存储
import re, requests, functools, urllib.parse

@service  #注册一个猫眼视频保存的服务 
def video_doorbell():
    entity = camera.loock_v06_d9c1_video_doorbell
    stream_address = urllib.parse.quote(getattr(entity, 'stream_address'))
    motion_video_time = re.sub(r'[^0-9]', '', getattr(entity, 'motion_video_time'))
    save_video(stream_address, motion_video_time)

async def save_video(stream_address, motion_video_time):
    post_request = functools.partial(requests.post, 'http://192.168.0.250:5005/save_video', data={"stream_address": stream_address, "motion_video_time": motion_video_time})
    response = await hass.async_add_executor_job(post_request)
    return response

监听

  • 实体状态变化:

    自动化开始的条件之一,某个传感器、开关、插座等状态发生变化时触发自动化;

#举个栗子:监听小爱收消息
@state_trigger("sensor.xiaomi_lx06_e7c0_conversation.timestamp") #最后一次收到消息时间
#@state_trigger("sensor.xiaomi_lx06_e7c0_conversation == '关闭窗帘'") 只能使用简单等、大于、小于、不等于这几个简单判断,复杂点的参考下面的
async def get_conversation():
    xiaoai_state_dict = state.getattr(sensor.xiaomi_lx06_e7c0_conversation) #获取小爱属性数据
    if "content" not in xiaoai_state_dict:
        log.error("获取内容失败")
        return
    xiaoai_stt = xiaoai_state_dict["content"] #获取小爱语音内容
    if xiaoai_stt is not None and "本地播放" in xiaoai_stt:
        music_msg = xiaoai_stt.replace("本地播放","").replace("的"," ").replace("歌曲","").replace("歌","")
    log.error(xiaoai_state_dict["content"]) #语音内容
  • MQTT消息:

    自动化开始条件之一,MQTT消息到达时触发自动化;

@mqtt_trigger(topic="home/bedroom/light/set", qos=1)
#@mqtt_trigger(str_expr="home/{room}/light/set")  str_expr和topic二选一,用来不同房间发来的消息
def do_everyone(msg):
    room = msg.data["room"]
    light_state = msg.payload
    pass

可选参数:   

  • topic:MQTT主题;
  • qos:MQTT消息质量;
  • retain:MQTT消息是否保留;
  • payload:MQTT消息内容;
  • str_expr:MQTT主题正则表达式;

条件判断

  • 时间范围:

    触发自动化条件时判断时间是否符合;

#举个栗子:监听小爱收消息
@state_trigger("sensor.xiaomi_lx06_e7c0_conversation.timestamp") #最后一次收到消息时间
@time_active("10:00", "12:00") #10点到12点之间
async def get_conversation():
    xiaoai_state_dict = state.getattr(sensor.xiaomi_lx06_e7c0_conversation) #获取小爱属性数据
    if "content" not in xiaoai_state_dict:
        log.error("获取内容失败")
        return
    log.error(xiaoai_state_dict["content"]) #语音内容
  • 数值范围:

    触发自动化条件时判断数值是否符合;

#举个栗子:监听小爱收消息
@state_trigger("sensor.xiaomi_lx06_e7c0_conversation.timestamp") #最后一次收到消息时间
@state_active("sensor.xiaomi_lx06_e7c0_conversation == '关闭窗帘'") #只有是 关闭窗帘 时触发
async def get_conversation():
    xiaoai_state_dict = state.getattr(sensor.xiaomi_lx06_e7c0_conversation) #获取小爱属性数据
    if "content" not in xiaoai_state_dict:
        log.error("获取内容失败")
        return
    log.error(xiaoai_state_dict["content"]) #语音内容

over

HACS-pyscript常用和特有的功能就这么多了,需要实现的功能就靠自己写if else了。
写完代码保存后新版HAOS会自动重载pyscript,然后就能在开发工具-服务中找到对应的服务了(自动化找不到),低版本需要手动重启HA。        

避坑

异步

在代码中,如果有涉及读写文件、请求网站等操作,需要使用async def来定义函数,否则会报错。
样例参考:      

import re, requests, functools, urllib.parse

@service  #注册一个猫眼视频保存的服务 
def video_doorbell():
    entity = camera.loock_v06_d9c1_video_doorbell
    stream_address = urllib.parse.quote(getattr(entity, 'stream_address'))
    motion_video_time = re.sub(r'[^0-9]', '', getattr(entity, 'motion_video_time'))
    save_video(stream_address, motion_video_time)

async def save_video(stream_address, motion_video_time):
    post_request = functools.partial(requests.post, 'http://192.168.0.250:5005/save_video', data={"stream_address": stream_address, "motion_video_time": motion_video_time})
    response = await hass.async_add_executor_job(post_request)
    return response

服务中使用多个实体

同时暂停多个实体:         

media_player.media_pause(entity_id=["media_player.xiaomi_lx06_e7c0_play_control", "media_player.yun_yin_le_xiao_ai_yin_xiang_5860"])

实践

米家猫眼视频本地存储

小爱音乐本地播放(需要Alist开启匿名访问,不想开匿名也可以加上认证)
游客,如果您要查看本帖隐藏内容请回复









评分

参与人数 6金钱 +71 HASS币 +20 收起 理由
kevinfzz + 5 高手,这是高手!
polisher + 16 论坛有你更精彩!
moz111 + 8
+ 20 + 20 膜拜大神!
隔壁的王叔叔 + 12 简直就是及时雨啊
relliky + 10 墙都不扶,就服楼主!

查看全部评分

技术宅拯救世界~~~
回复

使用道具 举报

0

主题

5

帖子

68

积分

注册会员

Rank: 2

积分
68
金钱
63
HASS币
0
发表于 2024-3-19 17:54:51 | 显示全部楼层
懂py的路过。参考一下,学习学习,谢谢up
回复

使用道具 举报

11

主题

247

帖子

1936

积分

金牌会员

Rank: 6Rank: 6

积分
1936
金钱
1689
HASS币
10
QQ
发表于 2024-3-19 18:36:27 | 显示全部楼层
这着实有点高级
回复

使用道具 举报

11

主题

287

帖子

2029

积分

金牌会员

Rank: 6Rank: 6

积分
2029
金钱
1742
HASS币
0
发表于 2024-3-19 19:25:29 | 显示全部楼层
我連pyscript是什麼都還不知道
回复

使用道具 举报

7

主题

1072

帖子

3361

积分

论坛元老

Rank: 8Rank: 8

积分
3361
金钱
2289
HASS币
0
发表于 2024-3-19 20:19:33 | 显示全部楼层
正好想学学,教程就来了。太棒了,感谢楼主分享。
回复

使用道具 举报

3

主题

96

帖子

654

积分

高级会员

Rank: 4

积分
654
金钱
558
HASS币
0
发表于 2024-3-19 21:49:10 | 显示全部楼层
先收藏一波
回复

使用道具 举报

11

主题

164

帖子

1806

积分

金牌会员

Rank: 6Rank: 6

积分
1806
金钱
1642
HASS币
0
发表于 2024-3-20 08:18:41 | 显示全部楼层
学习一下  
有个地方想更正下:相比NR采用轮询的方式获取状态
node-red-contrib-home-assistant-websocket节点event:state和event:all应该是以WebSocket API的方式监听event,所以也是事件驱动而非轮询  
https://zachowj.github.io/node-r ... e/events-state.html  
https://developers.home-assistan ... subscribe-to-events  
回复

使用道具 举报

0

主题

40

帖子

197

积分

注册会员

Rank: 2

积分
197
金钱
157
HASS币
0
发表于 2024-3-20 08:32:24 | 显示全部楼层
学习一下
回复

使用道具 举报

4

主题

60

帖子

516

积分

论坛技术达人

积分
516
金钱
446
HASS币
40
 楼主| 发表于 2024-3-20 09:00:59 | 显示全部楼层
735473216 发表于 2024-3-20 08:18
学习一下  
有个地方想更正下:相比NR采用轮询的方式获取状态
node-red-contrib-home-assistant-websocket ...

感谢,学习了  但是nr毕竟还是过了一层网络传输,直接python驱动还是有点优势的(也有可能单纯我的设备性能不行2333333
技术宅拯救世界~~~
回复

使用道具 举报

0

主题

33

帖子

182

积分

注册会员

Rank: 2

积分
182
金钱
149
HASS币
0
发表于 2024-3-20 10:05:17 | 显示全部楼层
高级了。
回复

使用道具 举报

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

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-4-29 19:24 , Processed in 0.095475 second(s), 35 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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