HA江苏电费插件 | 宁静致远

HA江苏电费插件

正在加载一言...


0.前言介绍


之前一直想把水电燃气都接入HA,水和燃气都接入了,电费一直没有接入。一开始尝试网上抓包,有个变量没摸透它的规律,所以没办法入手。后面发现小程序也可以查电费,所以准备从小程序入手,抓包看看能不能够有什么发现。

1.抓包准备


搜索微信小程序 国网江苏电力营业厅 抓下包。抓包的工具可以使用 Fiddler 苹果手机上可以使用 Stream 都是挺好用的。

2.抓包


在抓包的结果中找找有没有哪个返回金额等相关数据的。找了找,找到了 https://weixin.js.sgcc.com.cn/wxapp_dlsh/wx/oauth_executeNewMini.do?openid=xxxxxxxxx&timestamp=xxxxx&noncestr=xxxxxxx&sign=xxxxxxxx&unionid=xxxxxxx&userInfo=null 这个地址。通过多次对比这个链接,发现

openid=xxxxxxxxx //固定的
timestamp=xxxxx // 变动的

noncestr=xxxxxxx //变动的
sign=xxxxxxxx // 变动的
unionid=xxxxxxx //固定的

timestamp noncestr sign 这三个参数是有关联关系,不能够随意改动。这个地址的返回结果中也会返回这三个字段,所以我们不必考虑他们之间的关系,再次调用是,把之前的参数替换成现在最新的即可。

3.编写插件


分析到上面那步之后插件写起来就简单了,其实就是解析参数赋值的事情了。代码如下:

"""
Support for guojiadianwang
# Author:
    freefitter
# Created:
    2020/9/2
"""
import logging
import json
import configparser
import time, datetime
from dateutil.relativedelta import relativedelta 
from homeassistant.const import (
    CONF_API_KEY, CONF_NAME, ATTR_ATTRIBUTION, CONF_ID
    )
from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
import requests
from bs4 import BeautifulSoup
_Log=logging.getLogger(__name__)
DEFAULT_NAME = 'gjdw'
OPENID = 'openid'
NONCESTR = 'noncestr'
SIGN = 'sign'
UNIONID = 'unionid'
TIMESTAMP1 = 'timestamp'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
	vol.Required(OPENID): cv.string,
	vol.Required(NONCESTR): cv.string,
	vol.Required(SIGN): cv.string,
	vol.Required(UNIONID): cv.string,
	vol.Required(TIMESTAMP1): cv.string,
    vol.Optional(CONF_NAME, default= DEFAULT_NAME): cv.string,
})
HEADERS = {
    'Host': 'weixin.js.sgcc.com.cn',
    'Connection':  'keep-alive',
    'Content-Length': '0',
    'content-type': 'application/json',
    'Accept-Encoding': 'gzip,compress,br,deflate',
    'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.0(0x18000026) NetType/WIFI Language/zh_CN',
    'Referer': 'https://servicewechat.com/wx203b37ad2ad5d2a6/32/page-frame.html',
}
API_URL = "https://weixin.js.sgcc.com.cn/wxapp_dlsh/wx/oauth_executeNewMini.do"
def setup_platform(hass, config, add_devices, discovery_info=None):
    sensor_name = config.get(CONF_NAME)
    openid = config.get(OPENID)
    unionid = config.get(UNIONID)
    cfg = configparser.ConfigParser()
    cfg.read('param.ini')
    timestamp1 = cfg.get('Param', 'timestamp')
    noncestr = cfg.get('Param', 'noncestr')
    sign = cfg.get('Param', 'sign')
    
    url = API_URL + "?openid=" + openid +"&timestamp=" + timestamp1 + "&noncestr=" + noncestr + "&sign=" + sign + "&unionid=" + unionid + "&userInfo=null"    
    _Log.info("url:" + url )
    add_devices([GJDW(sensor_name,url,openid,unionid,cfg)])

class GJDW(Entity):
    """Representation of a guojiadianwang """
    def __init__(self,sensor_name,url,openid,unionid,cfg):
        self.attributes = {}
        self._state = None
        self._name = sensor_name
        self._url = url
        self._openid = openid
        self._unionid = unionid
        self._cfg = cfg
    @property
    def name(self):
        """Return the name of the sensor."""
        return self._name
    @property
    def state(self):
        """Return the state of the sensor."""
        return self._state
    @property
    def icon(self):
        """   mdi   ."""
        return 'mdi:flash'
    @property
    def device_state_attributes(self):
        """Return the state attributes."""
        return self.attributes
    @property
    def unit_of_measurement(self):
        """Return the unit this state is expressed in."""
        return "元"
    def update(self):
        try:
            """_Log.error("url:" + self._url)"""
            response = requests.post(self._url,headers = HEADERS)
        except ReadTimeout:
            _Log.error("Connection timeout....")
        except ConnectionError:
            _Log.error("Connection Error....")
        except RequestException:
            _Log.error("Unknown Error")
        res = response.content.decode('utf-8')
        if res.find("html") >= 0 :
            _Log.error("参数存在问题,请重新抓包填写")
            return
            """_Log.error(res)"""
        ret = json.loads(res)
        if ret["errcode"] == "0000":
            if "owe_amt" in ret["yeModel"] :
                self._state = round(float(ret["yeModel"]["owe_amt"]), 2)
                self.attributes['rca_flag'] = ret["yeModel"]["rca_flag"]
                self.attributes['cur_amt'] = ret["yeModel"]["cur_amt"]
                curfeeMsg = json.loads(ret["curfee"])
                self.attributes['ymd'] = curfeeMsg["ymd"]
                self.attributes['totalMoney'] = curfeeMsg["totalMoney"]
                self.attributes['powerSum'] = curfeeMsg["powerSum"]
                dbObjMsg = json.loads(ret["dbObj"])
                self.attributes['stepNo'] = dbObjMsg["stepNo"]
                self.attributes['yearAmount'] = dbObjMsg["yearAmount"]
                self.attributes['isUpdated'] = 'yes'
            else:
            	self.attributes['rca_flag'] = '-1'
            	self.attributes['cur_amt'] = '-1'
            	self.attributes['isUpdated'] = 'no'
            	#_Log.error("yeModel is null !")
            # 更新url参数 
            url = API_URL + "?openid=" + self._openid +"&timestamp=" + ret["timestamp"] + "&noncestr=" + ret["noncestr"] + "&sign=" + ret["sign"] + "&unionid=" + self._unionid + "&userInfo=null" 
            self._url = url
            # 保存参数至文本
            self._cfg.set("Param", "timestamp", ret["timestamp"])
            self._cfg.set("Param", "noncestr", ret["noncestr"])
            self._cfg.set("Param", "sign", ret["sign"])
            self._cfg.write(open('param.ini','w'))
        else:
            _Log.error("send request error....")

如果重启会导致参数失效,所以插件把变动的参数保存了下来,每次插件更新的时候将相关的参数更新进去,这样重启时间不长的话就不需要重新抓包了。

4.HA配置


在配置文件中加上如下内容:

- platform: gjdw
	name: '电费'
	openid: 'xxxxxxxxx-xxxxxxxxx'
	noncestr: 'xxxxxxxxx'
	sign: 'xxxxxxxxx'
	unionid: 'xxxxxxxxx'
	timestamp: 'xxxxxxxxx'

增加 param.ini 配置文件,文件内容如下

[Param]
timestamp = xxxxxxxxx
noncestr = xxxxxxxxx
sign = xxxxxxxxx

5.成果展示


插件展示

6.附件下载


点击下载 插件

或者去论坛我发的 电费插件-这次应该全国通用了吧 帖子下载。

7.结语


自己写写插件还是挺好玩的,最近玩了玩Node-Red和MQTT,感觉这个搞插件还是快的。有几个写好的插件可以先看看,抓了美食杰和微博热搜的数据

MQTT插件展示


文章作者: 彤爸比
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 彤爸比 !
评论
  目录