篇六:有些硬件太小众了怎么自己写插件
注意:如果没有任何开发经验不推荐自己改插件和写插件,毕竟这个也是个纯粹的技术活,和配置插件不一样,代码开发问题很多,包括调试,本文只是给有开发经验的一个写插件思路。
**
首先我们从分析插件开始把,毕竟写插件涉及的比较多。
安装开发环境
如果有开发需求推荐代码安装,方便调试。
注意:没有研发基础不推荐使用,涉及的知识面太多。
github下载比较慢,各显神通吧
git clone https://github.com/home-assistant/home-assistant.git
cd home-assistant && git checkout 0.97.2
开发工具
因为我这面有日常开发需求,所以用的是某家的全家桶,python我就用pycharm,有一些vscode熟的vscode也一样,我主要用pycharm调试功能,毕竟调试才是坑,arduino、esp开发最大的坑就是无法调试。
配置环境
然后配置虚拟环境,本地要有python环境。
然后ADD一个环境,如果没有什么特殊喜好的随意就行。
创建好了应该是一个空的虚拟环境,下面安装hass开发依赖。
切换分支: git pull && git checkout 0.97.2
换成你自己的版本。
安装依赖: pip3 install -r requirements_all.txt
,如果pip很慢去换个源,我都写过好几遍了就不写了。
开始调试插件
左侧这就是hass的代码结构,我们找个比较常用的插件把, hf-weather
,因为hass本身的调试方式比较复杂,我们取个巧,直接把第三方插件变成系统插件这样可以快速调试。
下载我改过那个插件
hf-weather:
https://github.com/meishild/hass-custom-components/tree/master/hf-weather/custom_components/hf-weather
把这个hf-weather目录放到 homeassistant
--> components
里
然后我们在 tests
里面新增一个 components-test
目录,然后新增一个测试用例,你们随意我叫 test_hf_weather.py
下面是测试代码:
import unittest
from time import sleep
from homeassistant.setup import setup_component
from tests.common import get_test_home_assistant
VALID_CONFIG = {
'weather': {
'platform': 'hf-weather',
'name': "testhome",
'location': 'local_ip',
'appkey': '你获取的key',
}}
class TestHfWeatherSensor(unittest.TestCase):
"""Test the sigfox platform."""
def setUp(self):
"""Initialize values for this testcase class."""
self.hass = get_test_home_assistant()
def tearDown(self):
"""Stop everything that was started."""
self.hass.stop()
def test_valid_credentials(self):
"""前面都不用管,那是初始化测试的hass环境的."""
assert setup_component(self.hass, 'weather', VALID_CONFIG)
assert len(self.hass.states.entity_ids()) == 2
然后去代码里打断点。
我打在这一行,我看一下他获取的数据结构是什么。
然后已debug方式启动测试用例,等着用例,因为这个插件是异步数据获取。等跳到断点。
我们看一下返回的整个weather_data:
就可以看到所有数据。
可以调试就代表可以方便改代码了,下面我们看看插件结构。
**
HASS自定义插件代码结构
代码解析
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_LOCATION): cv.string,
vol.Required(CONF_APPKEY): cv.string,
})
强制依赖三个参数:具体名字看上面的常量定义,基本上是名字、位置、appkey。
这是一个异步的setup会被hass系统调用。
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the hefeng weather."""
_LOGGER.info("setup platform weather.Heweather...")
# 获取我们需要的三个配置
name = config.get(CONF_NAME)
location = config.get(CONF_LOCATION)
appkey = config.get(CONF_APPKEY)
# 正则检查location是否是一个ip地址
is_ip = re.match(r"^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$", location) is not None
# 如果没有配置、或者配置local_ip
if location == "local_ip" or location is None or is_ip:
# 通过http协议获取请求结果
url = "https://api.ip.sb/geoip"
if is_ip:
url = url + "/" + location
import requests
ip = requests.get(url)
ip_data = ip.json()
# 根据返回结果组装location
lat = round(float(ip_data['latitude']), 2)
lon = round(float(ip_data['longitude']), 2)
location = "%f,%f" % (lat, lon)
if "," not in location:
raise PlatformNotReady("仅支持,ip、经纬度以及local_ip三种配置")
# 创建一个天气对象
data = WeatherData(hass, location, appkey)
# 这个是异步同步时间配置,就是多久调用一次。
yield from data.async_update(dt_util.now())
#data.async_update 这行最重要,其他都是浮云,代表定时刷新的时候刷新的这个接口。
async_track_time_interval(hass, data.async_update, TIME_BETWEEN_UPDATES)
# 异步增加HeFengWeather对象,这个是主要处理对象。
async_add_devices([HeFengWeather(data, name)], True)
这个接口里的代码无非就是去获取最新的数据。
# 通过HTTP访问,获取需要的信息
# 此处使用了基于aiohttp库的async_get_clientsession
try:
session = async_get_clientsession(self._hass)
weather_data = yield from self.async_weather(session, self._url, self._params)
uv = yield from self.async_uv(session, self._uv_url + self._location)
except(asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error while accessing: %s", self._url)
return
然后刷新对象内的变量值。
如果想写插件
插件类型说明
因为hass的限制,首先你得弄明白你要写的是个什么,因为官方一共就那么几种类型,你必须合并到一个类型里,我举几个例子。
- 我的这个东西只是看看状态的,比如天气、信息、传感器,那么都算sensor的,如果是只有两个状态那就是binary_sensor
- 我这个想能控制,基本上也可以拆分成,我控制的是二元的就是开关,那都算switch,哪怕是红外这种其实也是swith,无非是开和关是一样的。
- 什么空气净化器啊、xx扇啊,是几个类型高、中、低、自动的都是fan。
- 如果你这是一个组合设备,那就拆成多个,比如多个sensor、一个fan。
- 不管是什么天气都是weather。
还有一个所谓的拆成多个的实际上在系统注册是实际是独立分开的对象。
比如我写那个西门子空气质量检测的,我有这么多个类型。
SENSOR_TYPES = {
'temperature': ['temperature', TEMP_CELSIUS],
'humidity': ['humidity', '%'],
'pm2.5': ['pm2.5', 'μg/m³'],
'pm10': ['pm10', 'μg/m³'],
'pm1.0': ['pm1.0', 'μg/m³'],
'hcho': ['hcho', 'ppm'],
}
# 安装方法
async def async_setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup."""
serial_no = config.get(CONF_SERIAL_NO)
dev = []
api = CallAPI(async_create_clientsession(hass), hass.loop, serial_no)
data = await api.update()
if 'code' not in data or data['code'] != 200:
raise PlatformNotReady()
for variable in config[CONF_DISPLAY_OPTIONS]:
dev.append(SiemensSensor(api, variable))
add_devices_callback(dev, True)
你就代表我有这么多个SiemensSensor对象,对象的更新方法去刷新数据然后放到各个对象的数据里,通过hass的默认方法取走。
如果不想弄初这么多个,那也可以合并成一个然后通过 device_state_attributes
一次给出去,不过这样就要通过配置来拆分数据了。
我能写什么样的插件
基本上分几个类型。
我有一个硬件的api
那就直接通过http协议或者其他协议请求的方式直接发起请求,然后测试。
我有一个硬件但是不知道能不能做
这个就比较麻烦了涉及请求拦截,然后参数解析拼装,算是爬虫的一部分能力。
我是用下面这个花瓶,然后通过手机代理将http协议发送都电脑上进行解析,然后在通过http协议模拟测试工具测试,最终才能使用,而且现在很多都是https的协议还要给手机安装根证书才行,这些都需要有爬虫或者稍微高端的客户端测试能力了。