1 Commits
Author SHA1 Message Date
HassBox a1e5993fd1 deploy 2024-05-09 00:08:18 +08:00
11 changed files with 606 additions and 563 deletions
+29 -1
View File
@@ -1 +1,29 @@
# State Grid # 国家电网
集成国家电网每日用电等数据,支持多户号,阶梯用电。
## 安装/更新
#### 方法 1: 通过`Samba`或`SFTP`手动安装
下载并复制`custom_components/state_gird`文件夹到 Home Assistant 根目录下的`custom_components`文件夹
#### 方法 2: 通过`SSH`或`Terminal & SSH`加载项执行一键安装命令
```shell
curl -fsSL get.hassbox.cn/state_grid | bash
```
#### 方法 3: 通过`shell_command`服务
1. 复制下面的代码到 Home Assistant 配置文件`configuration.yaml`
```yaml
shell_command:
install_state_grid: |-
curl -fsSL get.hassbox.cn/state_grid | bash
```
2. 重启 Home Assistant
3. 在 Home Assistant 开发者工具中调用此服务[`service: shell_command.install_state_grid`](https://my.home-assistant.io/redirect/developer_call_service/?service=shell_command.install_state_grid)
+3 -1
View File
@@ -11,5 +11,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
"""Set up this integration using UI.""" """Set up this integration using UI."""
config = await async_load_from_store(hass, "state_grid.config") or None config = await async_load_from_store(hass, "state_grid.config") or None
hass.data[DOMAIN] = StateGridDataClient(hass = hass, config = config) hass.data[DOMAIN] = StateGridDataClient(hass = hass, config = config)
await hass.config_entries.async_forward_entry_setups(config_entry, [Platform.SENSOR]) hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, Platform.SENSOR)
)
return True return True
+2 -5
View File
@@ -139,7 +139,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
else: else:
self.data_client.refresh_interval = int(user_input['refresh_interval']) self.data_client.refresh_interval = int(user_input['refresh_interval'])
self.data_client.is_debug = user_input['is_debug'] self.data_client.is_debug = user_input['is_debug']
await self.data_client.save_data()
return self.async_create_entry( return self.async_create_entry(
title="国家电网", title="国家电网",
data={} data={}
@@ -202,13 +201,13 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
async def async_step_check_qr_code(self, user_input=None): async def async_step_check_qr_code(self, user_input=None):
result = await self.data_client.check_qr_code() result = await self.data_client.check_qr_code()
if result['errcode'] != 0: if result['errcode'] != 0:
return self.async_abort( return self.async_abort(
reason="check_qr_code_error", reason="check_qr_code_error",
description_placeholders={'errmsg': result["errmsg"]} description_placeholders={'errmsg': result["errmsg"]}
) )
await self.data_client.refresh_data(force_refresh=True)
await self.data_client.coordinator.async_request_refresh()
return self.async_create_entry( return self.async_create_entry(
title="国家电网", title="国家电网",
data={} data={}
@@ -248,8 +247,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
code = user_input['code'] code = user_input['code']
result = await self.data_client.verfiy_phone_code(code) result = await self.data_client.verfiy_phone_code(code)
if result['errcode'] == 0: if result['errcode'] == 0:
await self.data_client.refresh_data(force_refresh=True)
await self.data_client.coordinator.async_request_refresh()
return self.async_create_entry( return self.async_create_entry(
title="国家电网", title="国家电网",
data={} data={}
+2 -2
View File
@@ -1,4 +1,4 @@
DOMAIN = "state_grid" DOMAIN = 'state_grid'
PACKAGE_NAME = "custom_components.state_grid" PACKAGE_NAME = "custom_components.state_grid"
VERSION = "0.1.4" VERSION = "0.0.5"
VERSION_STORAGE = 1 VERSION_STORAGE = 1
+238 -303
View File
@@ -1,132 +1,110 @@
_B5='get_door_daily_bill_api' _Al='queryYear'
_B4='startTime' _Ak='provinceCode'
_B3='constType' _Aj='sceneType'
_B2='provinceCode' _Ai='consNoSrc'
_B1='access_token' _Ah='access_token'
_B0='businessType' _Ag='businessType'
_A_='qrCodeSerial' _Af='qrCodeSerial'
_Az='redirect_url' _Ae='redirect_url'
_Ay='_access_token' _Ad='_access_token'
_Ax='dataVersion' _Ac='refresh_interval'
_Aw='refresh_interval' _Ab='doorAccountDict'
_Av='doorAccountDict' _Aa='refreshToken'
_Au='refreshToken' _AZ='accessToken'
_At='accessToken' _AY='/osg-web0004/member/c24/f01'
_As='/osg-web0004/member/c24/f01' _AX='BCP_00026'
_Ar='BCP_00026' _AW='serviceCode_smt'
_Aq='serviceCode_smt' _AV='WEBA10070900'
_Ap='WEBA10070900' _AU='serviceType'
_Ao='serviceType' _AT='jM_custType'
_An='jM_custType' _AS='jM_busiTypeCode'
_Am='jM_busiTypeCode' _AR='consType'
_Al='doorNumberManeger' _AQ='constType'
_Ak='month_t_ele_num' _AP='powerUserList'
_Aj='month_n_ele_num' _AO='publicKey'
_Ai='month_v_ele_num' _AN='WEBA10070800'
_Ah='month_p_ele_num' _AM='timeDay'
_Ag='month_ele_num' _AL='WEBA10070700'
_Af='thisTPq' _AK='0101143'
_Ae='thisNPq' _AJ='channelNo'
_Ad='thisVPq' _AI='latestBillMonth'
_Ac='thisPPq' _AH='yearTotalCost'
_Ab='%Y%m%d' _AG='userAccountId'
_Aa='params4' _AF='0000'
_AZ='params3' _AE='refresh_token'
_AY='params1' _AD='skey'
_AX='queryYear' _AC='userInfo'
_AW='userAccountId' _AB='0101046'
_AV='elecTypeCode' _AA='proCode'
_AU='powerUserList' _A9='loginAccount'
_AT='publicKey' _A8='keyCode'
_AS='WEBA10070800' _A7='querytypeCode'
_AR='timeDay' _A6='01010049'
_AQ='WEBA10070700' _A5='doorNumberManeger'
_AP='channelNo' _A4='monthBillList'
_AO='dayElePq' _A3='accountBalance'
_AN='sevenEleList' _A2='list'
_AM='month' _A1='userName'
_AL='yearTotalCost' _A0='acctId'
_AK='consType' _z='resultCode'
_AJ='0000' _y='quInfo'
_AI='refresh_token' _x='BCP_000026'
_AH='skey' _w='app'
_AG='userInfo' _v='WEBALIPAY_01'
_AF='0101046' _u='order'
_AE='billYear' _t='state_grid'
_AD='proCode' _s=False
_AC='loginAccount' _r='authFlag'
_AB='list' _q='09'
_AA='keyCode' _p='0101183'
_A9='querytypeCode' _o='stepelect'
_A8='01010049' _n='account'
_A7='account' _m='consNo_dst'
_A6='daily_bill_list' _l='token'
_A5='account_balance' _k='0101154'
_A4='provinceId' _j='getday'
_A3='userName' _i='consNo'
_A2='acctId' _h='bizrt'
_A1='resultCode' _g='clearCache'
_A0='BCP_000026' _f='timestamp'
_z='app' _e='errmsg'
_y='WEBALIPAY_01' _d='promotCode'
_x='order' _c='01'
_w='state_grid' _b='SGAPP'
_v='latestBillMonth' _a='devciceId'
_u='quInfo' _Z='devciceIp'
_t='authFlag' _Y='orgNo'
_s='09' _X='tenant'
_r='0101183' _W='member'
_q='stepelect' _V='daily_bill_list'
_p='consNo' _U='proNo'
_o=True _T='promotType'
_n='0101154' _S='target'
_m='monthBillList'
_l='bizrt'
_k='token'
_j='timestamp'
_i=False
_h='month_ele'
_g='consNo_dst'
_f='errmsg'
_e='clearCache'
_d='SGAPP'
_c='orgNo'
_b='promotCode'
_a='01'
_Z='devciceId'
_Y='devciceIp'
_X='proNo'
_W='tenant'
_V='member'
_U='promotType'
_T='getday'
_S='srvrt'
_R='userId' _R='userId'
_Q='subBusiTypeCode' _Q='srvrt'
_P='target' _P='subBusiTypeCode'
_O='serCat' _O='serCat'
_N='serialNo' _N='serialNo'
_M='0902' _M='0902'
_L='srvCode' _L='srvCode'
_K='busiTypeCode' _K='uscInfo'
_J='uscInfo' _J=None
_I='channelCode' _I='busiTypeCode'
_H='code' _H='code'
_G='1' _G='channelCode'
_F=None _F='1'
_E='source' _E='source'
_D='funcCode' _D='funcCode'
_C='errcode' _C='serviceCode'
_B='serviceCode' _B='errcode'
_A='data' _A='data'
import json,time,aiohttp,urllib.parse,datetime import json,time,aiohttp,urllib.parse,datetime
from.const import VERSION
from.utils.logger import LOGGER from.utils.logger import LOGGER
from.utils.store import async_save_to_store from.utils.store import async_save_to_store
from.utils.crypt import a,b,c,d,e from.utils.crypt import a,b,c,d,e
configuration={_J:{_V:_M,_Y:'',_Z:'',_W:_w},_E:_d,_P:'32101',_I:_M,_AP:_M,'toPublish':_a,'siteId':'2012000000033700',_L:'',_N:'',_D:'',_B:{_x:_n,'uploadPic':'0101296','pauseSCode':'0101250','pauseTCode':'0101251','listconsumers':'0101093','messageList':'0101343','submit':'0101003','sbcMsg':'0101210','powercut':'0104514','BkAuth01':'f15','BkAuth02':'f18','BkAuth03':'f02','BkAuth04':'f17','BkAuth05':'f05','BkAuth06':'f16','BkAuth07':'f01','BkAuth08':'f03'},'electricityArchives':{'servicecode':'0104505',_E:_M},'subscriptionList':{_L:'APP_SGPMS_05_030',_N:'22',_I:_M,_D:'22',_P:'-1'},'userInformation':{_B:'01008183',_E:_d},'userInform':{_B:_r,_E:_d},'elesum':{_I:_M,_D:_y,_b:_G,_U:_G,_B:'0101143',_E:_z},_A7:{_I:_M,_D:'WEBA1007200'},_Al:{_E:_M,_P:'-1',_I:_s,_AP:_s,_B:_A8,_D:'WEBA40050000',_J:{_V:_M,_Y:'',_Z:'',_W:_w}},'doorAuth':{_E:_d,_B:'f04'},'xinZ':{_O:'101',_Am:'101','fJ_busiTypeCode':'102',_An:'03','fJ_custType':'02',_Ao:_a,_Q:'',_D:_AQ,_x:_n,_E:_d,_A9:_G},'onedo':{_B:_AF,_E:_d,_D:_AQ,'queryType':'03'},'xinHuTongDian':{_O:'110',_K:'211',_Q:'21102',_D:'WEBA10071200',_I:_M,_E:_s,_B:_r},'company':{_O:'104',_D:_AQ,_Ao:'02',_A9:_G,_t:_G,_E:_d,_x:_n},'charge':{_I:_s,_D:'WEBA10071300',_AP:'0901',_O:'102',_An:_a,_Am:'102'},'other':{_I:_s,_D:'WEBA10079700',_O:'129',_K:'999',_Q:'21501',_B:_A0,_L:'',_N:''},'vatchange':{'submit':'0101003',_K:'320',_Q:'',_O:'115',_D:'WEBA10074000',_t:_G},'bill':{_e:_G,_D:_y,_U:_G,_B:_A0},_q:{_I:_M,_D:_y,_U:_G,_e:_s,_B:_A0,_E:_z},_T:{_I:_M,_e:'11',_D:_y,_b:_G,_U:_G,_B:_A0,_E:_z},'mouthOut':{_I:_M,_e:'11',_D:_y,_b:_G,_U:_G,_B:_A0,_E:_z},'meter':{_O:'114',_K:'304',_D:'WEBA10071000',_Q:'',_B:_AF,_N:''},'complaint':{_K:'005','srvMode':_M,'anonymousFlag':'0','replyMode':_a,'retvisitFlag':_a},'report':{_K:'006'},'tradewinds':{_K:'019'},'somesay':{_K:'091'},'faultrepair':{_D:_Ap,_B:_r,_O:'111',_K:'001',_Q:'21505'},'electronicInvoice':{_O:'105',_K:'0'},'rename':{_B:_AF,_D:'WEBA10076100',_K:'210',_O:'109',_t:_G,'gh_busiTypeCode':'211','gh_subusi':'21101',_N:'',_L:''},'pause':{_Q:'',_B:_A8,_D:'WEBA10073600',_O:'107',_K:'203','jr_busi':'201',_N:'',_L:''},'capacityRecovery':{_B:_A8,_E:_d,_L:'',_N:'',_D:'WEBA10073700','busiTypeCode_stop':'204','busiTypeCode_less':'202',_K:'202',_Q:'',_O:'108',_AR:'5',_t:_G},'electricityPriceChange':{_B:_r,_K:'215',_Q:'21502',_O:'113',_t:_G,_AR:'15',_D:'WEBA10073900WEB',_L:'',_N:''},'electricityPriceStrategyChange':{_B:'01008183',_K:'215',_Q:'21506',_O:'160',_D:'WEBV00000517WEB',_L:'',_N:''},'eemandValueAdjustment':{_B:_r,_L:'',_N:'',_O:'112',_D:'WEBA10073800',_K:'215',_Q:'21504',_t:_G,_AR:'5','getMonthServiceCode':_AF},'businessProgress':{_B:_r,_L:_a,_D:'WEB01'},'increase':{_E:_d,_N:'',_L:'',_Aq:_A8,_B:_n,_x:_n,_D:_AS,_A9:_G,_O:'106',_K:'111',_Q:''},'fjincrea':{_O:'105',_K:'110',_Q:'',_E:_d,_D:_AS,_N:'',_L:'',_Aq:_A8,_B:_n,_x:_n,_A9:_G},'persIncrea':{_O:'105',_K:'109',_x:_n,_Q:'',_E:_d,_D:_AS,_A9:_G},'fgdChange':{_B:_r,_L:_a,_I:_s,_D:_Ap,_K:'215',_Q:'21505',_O:'111',_t:_G},'createOrder':{_I:_M,_D:_y,_L:'BCP_000001','chargeMode':'02','conType':_a,'bizTypeId':'BT_ELEC'},'largePopulation':{_K:'383',_D:'WEBA10076800',_Q:'',_L:'',_U:'',_b:'',_I:'0901',_O:'383',_B:'',_N:''},'biaoJiCode':{_B:'0104507',_E:'1704',_I:'1704'},'twoGuar':{_K:'402',_Q:'40201',_D:'web_twoGuar'},'electTrend':{_B:_Ar,_I:_M},'emergency':{_B:_Ar,_D:'A10000000',_I:_M},'infoPublic':{_B:'2545454',_E:_z}}
appKey='3def6c365d284881bf1a9b2b502ee68c' appKey='3def6c365d284881bf1a9b2b502ee68c'
appSecret='ab7357dae64944a197ace37398897f64' appSecret='ab7357dae64944a197ace37398897f64'
configuration={_K:{_W:_M,_Z:'',_a:'',_X:_t},_E:_b,_S:'32101',_G:_M,_AJ:_M,'toPublish':_c,'siteId':'2012000000033700',_L:'',_N:'',_D:'',_C:{_u:_k,'uploadPic':'0101296','pauseSCode':'0101250','pauseTCode':'0101251','listconsumers':'0101093','messageList':'0101343','submit':'0101003','sbcMsg':'0101210','powercut':'0104514','BkAuth01':'f15','BkAuth02':'f18','BkAuth03':'f02','BkAuth04':'f17','BkAuth05':'f05','BkAuth06':'f16','BkAuth07':'f01','BkAuth08':'f03'},'electricityArchives':{'servicecode':'0104505',_E:_M},'subscriptionList':{_L:'APP_SGPMS_05_030',_N:'22',_G:_M,_D:'22',_S:'-1'},'userInformation':{_C:'01008183',_E:_b},'userInform':{_C:_p,_E:_b},'elesum':{_G:_M,_D:_v,_d:_F,_T:_F,_C:_AK,_E:_w},_n:{_G:_M,_D:'WEBA1007200'},_A5:{_E:_M,_S:'-1',_G:_q,_AJ:_q,_C:_A6,_D:'WEBA40050000',_K:{_W:_M,_Z:'',_a:'',_X:_t}},'doorAuth':{_E:_b,_C:'f04'},'xinZ':{_O:'101',_AS:'101','fJ_busiTypeCode':'102',_AT:'03','fJ_custType':'02',_AU:_c,_P:'',_D:_AL,_u:_k,_E:_b,_A7:_F},'onedo':{_C:_AB,_E:_b,_D:_AL,'queryType':'03'},'xinHuTongDian':{_O:'110',_I:'211',_P:'21102',_D:'WEBA10071200',_G:_M,_E:_q,_C:_p},'company':{_O:'104',_D:_AL,_AU:'02',_A7:_F,_r:_F,_E:_b,_u:_k},'charge':{_G:_q,_D:'WEBA10071300',_AJ:'0901',_O:'102',_AT:_c,_AS:'102'},'other':{_G:_q,_D:'WEBA10079700',_O:'129',_I:'999',_P:'21501',_C:_x,_L:'',_N:''},'vatchange':{'submit':'0101003',_I:'320',_P:'',_O:'115',_D:'WEBA10074000',_r:_F},'bill':{_g:_F,_D:_v,_T:_F,_C:_x},_o:{_G:_M,_D:_v,_T:_F,_g:_q,_C:_x,_E:_w},_j:{_G:_M,_g:'11',_D:_v,_d:_F,_T:_F,_C:_x,_E:_w},'mouthOut':{_G:_M,_g:'11',_D:_v,_d:_F,_T:_F,_C:_x,_E:_w},'meter':{_O:'114',_I:'304',_D:'WEBA10071000',_P:'',_C:_AB,_N:''},'complaint':{_I:'005','srvMode':_M,'anonymousFlag':'0','replyMode':_c,'retvisitFlag':_c},'report':{_I:'006'},'tradewinds':{_I:'019'},'somesay':{_I:'091'},'faultrepair':{_D:_AV,_C:_p,_O:'111',_I:'001',_P:'21505'},'electronicInvoice':{_O:'105',_I:'0'},'rename':{_C:_AB,_D:'WEBA10076100',_I:'210',_O:'109',_r:_F,'gh_busiTypeCode':'211','gh_subusi':'21101',_N:'',_L:''},'pause':{_P:'',_C:_A6,_D:'WEBA10073600',_O:'107',_I:'203','jr_busi':'201',_N:'',_L:''},'capacityRecovery':{_C:_A6,_E:_b,_L:'',_N:'',_D:'WEBA10073700','busiTypeCode_stop':'204','busiTypeCode_less':'202',_I:'202',_P:'',_O:'108',_AM:'5',_r:_F},'electricityPriceChange':{_C:_p,_I:'215',_P:'21502',_O:'113',_r:_F,_AM:'15',_D:'WEBA10073900WEB',_L:'',_N:''},'electricityPriceStrategyChange':{_C:'01008183',_I:'215',_P:'21506',_O:'160',_D:'WEBV00000517WEB',_L:'',_N:''},'eemandValueAdjustment':{_C:_p,_L:'',_N:'',_O:'112',_D:'WEBA10073800',_I:'215',_P:'21504',_r:_F,_AM:'5','getMonthServiceCode':_AB},'businessProgress':{_C:_p,_L:_c,_D:'WEB01'},'increase':{_E:_b,_N:'',_L:'',_AW:_A6,_C:_k,_u:_k,_D:_AN,_A7:_F,_O:'106',_I:'111',_P:''},'fjincrea':{_O:'105',_I:'110',_P:'',_E:_b,_D:_AN,_N:'',_L:'',_AW:_A6,_C:_k,_u:_k,_A7:_F},'persIncrea':{_O:'105',_I:'109',_u:_k,_P:'',_E:_b,_D:_AN,_A7:_F},'fgdChange':{_C:_p,_L:_c,_G:_q,_D:_AV,_I:'215',_P:'21505',_O:'111',_r:_F},'createOrder':{_G:_M,_D:_v,_L:'BCP_000001','chargeMode':'02','conType':_c,'bizTypeId':'BT_ELEC'},'largePopulation':{_I:'383',_D:'WEBA10076800',_P:'',_L:'',_T:'',_d:'',_G:'0901',_O:'383',_C:'',_N:''},'biaoJiCode':{_C:'0104507',_E:'1704',_G:'1704'},'twoGuar':{_I:'402',_P:'40201',_D:'web_twoGuar'},'electTrend':{_C:_AX,_G:_M},'emergency':{_C:_AX,_D:'A10000000',_G:_M},'infoPublic':{_C:'2545454',_E:_w}}
baseApi='https://www.95598.cn/api' baseApi='https://www.95598.cn/api'
get_request_key_api='/oauth2/outer/c02/f02' get_request_key_api='/oauth2/outer/c02/f02'
get_qr_code_api='/osg-open-uc0001/member/c8/f24' get_qr_code_api='/osg-open-uc0001/member/c8/f24'
@@ -142,64 +120,51 @@ get_door_number_api='/osg-open-uc0001/member/c9/f02'
get_door_balance_api='/osg-open-bc0001/member/c05/f01' get_door_balance_api='/osg-open-bc0001/member/c05/f01'
get_door_bill_api='/osg-open-bc0001/member/c01/f02' get_door_bill_api='/osg-open-bc0001/member/c01/f02'
get_door_ladder_api='/osg-open-bc0001/member/c04/f03' get_door_ladder_api='/osg-open-bc0001/member/c04/f03'
getJiaoFeiRecordApi=_As getJiaoFeiRecordApi=_AY
get_door_daily_bill_api=_As get_door_daily_bill_api=_AY
sessionIdControlApiList=[get_qr_code_api,get_qr_code_status_api,get_qr_code_token_api,send_code_api,code_login_api] sessionIdControlApiList=[get_qr_code_api,get_qr_code_status_api,get_qr_code_token_api,send_code_api,code_login_api]
keyCodeControlApiList=[get_qr_code_status_api,get_qr_code_token_api,send_code_api,code_login_api,getCertificationApi,get_request_authorize_api,get_web_token_api,refresh_web_token_api,get_door_number_api,get_door_balance_api,get_door_bill_api,get_door_ladder_api,getJiaoFeiRecordApi,get_door_daily_bill_api] keyCodeControlApiList=[get_qr_code_status_api,get_qr_code_token_api,send_code_api,code_login_api,getCertificationApi,get_request_authorize_api,get_web_token_api,refresh_web_token_api,get_door_number_api,get_door_balance_api,get_door_bill_api,get_door_ladder_api,getJiaoFeiRecordApi,get_door_daily_bill_api]
authControlApiList=[get_door_number_api,get_door_balance_api,get_door_bill_api,get_door_ladder_api,getJiaoFeiRecordApi,get_door_daily_bill_api] authControlApiList=[get_door_number_api,get_door_balance_api,get_door_bill_api,get_door_ladder_api,getJiaoFeiRecordApi,get_door_daily_bill_api]
tControlApiList=[getCertificationApi,get_door_balance_api,get_door_bill_api,get_door_ladder_api,getJiaoFeiRecordApi,get_door_daily_bill_api] tControlApiList=[getCertificationApi,get_door_balance_api,get_door_bill_api,get_door_ladder_api,getJiaoFeiRecordApi,get_door_daily_bill_api]
def json_dumps(data):return json.dumps(data,separators=(',',':'),ensure_ascii=_i) def json_dumps(data):return json.dumps(data,separators=(',',':'),ensure_ascii=_s)
def normal_round(num,ndigits=0):
A=ndigits
if A==0:return int(num+.5)
else:B=10**A;return int(num*B+.5)/B
def catchFloat(data,key): def catchFloat(data,key):
if key in data: if key in data:
try:return normal_round(float(data[key]),2) try:return float(data[key])
except:return 0 except:return 0
else:return 0 else:return 0
class StateGridDataClient: class StateGridDataClient:
hass=_F;coordinator=_F;session=_F;dataVersion=_F;keyCode=_F;publicKey=_F;need_login=_i;retry_times=0;phone=_F;codeKey=_F;serialNo=_F;qrCodeSerial=_F;userInfo=_F;accountInfo=_F;powerUserList=_F;doorAccountDict={};cookie=[];timestamp=int(time.time()*1000);accessToken=_F;refreshToken=_F;token=_F;expirationDate=_F;refresh_interval=6;is_debug=_i hass=_J;session=_J;keyCode=_J;publicKey=_J;need_login=_s;phone=_J;codeKey=_J;serialNo=_J;qrCodeSerial=_J;userInfo=_J;accountInfo=_J;powerUserList=_J;doorAccountDict={};cookie=[];timestamp=int(time.time()*1000);accessToken=_J;refreshToken=_J;token=_J;expirationDate=_J;refresh_interval=12;is_debug=_s
def __init__(A,hass,config=_F): def __init__(A,hass,config=_J):
B=config;A.hass=hass;C=aiohttp.TCPConnector(ssl=_i);D=aiohttp.CookieJar(quote_cookie=_o);A.session=aiohttp.ClientSession(cookie_jar=D,connector=C) B=config;A.hass=hass;C=aiohttp.TCPConnector(ssl=_s);D=aiohttp.CookieJar(quote_cookie=True);A.session=aiohttp.ClientSession(cookie_jar=D,connector=C)
if B is not _F: if B is not _J:
try:A.keyCode=B[_AA];A.publicKey=B[_AT];A.accessToken=B[_At];A.refreshToken=B[_Au];A.token=B[_k];A.userInfo=B[_AG];A.powerUserList=B[_AU];A.doorAccountDict=B[_Av];A.refresh_interval=B[_Aw];A.is_debug=B['is_debug'];A.dataVersion=B[_Ax] try:A.keyCode=B[_A8];A.publicKey=B[_AO];A.accessToken=B[_AZ];A.refreshToken=B[_Aa];A.token=B[_l];A.userInfo=B[_AC];A.powerUserList=B[_AP];A.doorAccountDict=B[_Ab];A.refresh_interval=B[_Ac];A.is_debug=B['is_debug']
except Exception as E:LOGGER.error(E) except Exception as E:LOGGER.error(E)
async def save_data(B):A={};A[_AA]=B.keyCode;A[_AT]=B.publicKey;A[_At]=B.accessToken;A[_Au]=B.refreshToken;A[_k]=B.token;A[_AG]=B.userInfo;A[_AU]=B.powerUserList;A[_Av]=B.doorAccountDict;A[_Aw]=B.refresh_interval;A['is_debug']=B.is_debug;A[_Ax]=VERSION;await async_save_to_store(B.hass,'state_grid.config',A) async def save_data(A):B={};B[_A8]=A.keyCode;B[_AO]=A.publicKey;B[_AZ]=A.accessToken;B[_Aa]=A.refreshToken;B[_l]=A.token;B[_AC]=A.userInfo;B[_AP]=A.powerUserList;B[_Ab]=A.doorAccountDict;B[_Ac]=A.refresh_interval;B['is_debug']=A.is_debug;await async_save_to_store(A.hass,'state_grid.config',B)
def encrypt_post_data(A,data):B={_Ay:A.accessToken[len(A.accessToken)//2:]if A.accessToken else'','_t':A.token[len(A.token)//2:]if A.token else'','_data':data,_j:A.timestamp};return A.encrypt_wapper_data(B) def encrypt_post_data(A,data):B={_Ad:A.accessToken[len(A.accessToken)//2:]if A.accessToken else'','_t':A.token[len(A.token)//2:]if A.token else'','_data':data,_f:A.timestamp};return A.encrypt_wapper_data(B)
def encrypt_wapper_data(A,data):B=a(json_dumps(data),A.keyCode);return{_A:B+c(B+str(A.timestamp)),_AH:d(A.keyCode,A.publicKey),_j:str(A.timestamp)} def encrypt_wapper_data(A,data):B=a(json_dumps(data),A.keyCode);return{_A:B+c(B+str(A.timestamp)),_AD:d(A.keyCode,A.publicKey),_f:str(A.timestamp)}
def handle_request_result_message(E,api,result): def handle_request_result_message(E,api,result):
D='message';C='resultMessage';A=result;B=_F D='message';C='resultMessage';A=result;B=_J
if _A in A and _S in A[_A]and C in A[_A][_S]:B=A[_A][_S][C] if _A in A and _Q in A[_A]and C in A[_A][_Q]:B=A[_A][_Q][C]
elif _S in A and C in A[_S]:B=A[_S][C] elif _Q in A and C in A[_Q]:B=A[_Q][C]
elif D in A:B=A[D] elif D in A:B=A[D]
else:B=json_dumps(A) else:B=json_dumps(A)
if E.is_debug:LOGGER.error(api+': '+B);LOGGER.error(json_dumps(A)) if E.is_debug:LOGGER.error(api+': '+B);LOGGER.error(json_dumps(A))
return B return B
async def __fetch_safe(C,api,data): async def fetch(B,api,data,header=_J):
B=await C.__fetch(api,data)
if _H not in B:return B
A=B[_H]
if 10015==A or 10108==A or 10009==A or 10207==A or 10005==A or 10010==A or 30010==A or 10002==A:
await C.__refresh_token()
if C.need_login is _o:return B
else:return await C.__fetch(api,data)
else:return B
async def __fetch(B,api,data,header=_F):
T='encryptData';S='464606a4-184c-4beb-b442-2ab7761d0796';R='key_code';Q='state';P='sign';O='grant_type';N='application/json;charset=UTF-8';M='Content-Type';L=header;K='client_secret';I='client_id';E=api;B.timestamp=int(time.time()*1000);D=B.timestamp T='encryptData';S='464606a4-184c-4beb-b442-2ab7761d0796';R='key_code';Q='state';P='sign';O='grant_type';N='application/json;charset=UTF-8';M='Content-Type';L=header;K='client_secret';I='client_id';E=api;B.timestamp=int(time.time()*1000);D=B.timestamp
if B.keyCode is _F:B.keyCode=e(32,16,2) if B.keyCode is _J:B.keyCode=e(32,16,2)
F=B.keyCode;G={'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36','Accept':N,M:N,'version':'1.0',_E:'0901',_j:str(D),'wsgwType':'web','appKey':appKey};A=data F=B.keyCode;G={'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36','Accept':N,M:N,'version':'1.0',_E:'0901',_f:str(D),'wsgwType':'web','appKey':appKey};A=data
if E==get_request_key_api:A={I:appKey,K:appSecret};H=a(json_dumps(A),F);A={_A:H+c(H+str(D)),_AH:d(F,'042BC7AD510BF9793B7744C8854C56A8C95DD1027EE619247A332EC6ED5B279F435A23D62441FE861F4B0C963347ECD5792F380B64CA084BE8BE41151F8B8D19C8'),I:appKey,_j:str(D)} if E==get_request_key_api:A={I:appKey,K:appSecret};H=a(json_dumps(A),F);A={_A:H+c(H+str(D)),_AD:d(F,'042BC7AD510BF9793B7744C8854C56A8C95DD1027EE619247A332EC6ED5B279F435A23D62441FE861F4B0C963347ECD5792F380B64CA084BE8BE41151F8B8D19C8'),I:appKey,_f:str(D)}
elif E==get_qr_code_api:A={_Ay:'','_t':'','_data':A,_j:D} elif E==get_qr_code_api:A={_Ad:'','_t':'','_data':A,_f:D}
elif E==get_request_authorize_api: elif E==get_request_authorize_api:
A={I:appKey,'response_type':_H,_Az:'/test',_j:D,'rsi':B.token};A=urllib.parse.urlencode(A);G[M]='application/x-www-form-urlencoded; charset=UTF-8';G[_AA]=F A={I:appKey,'response_type':_H,_Ae:'/test',_f:D,'rsi':B.token};A=urllib.parse.urlencode(A);G[M]='application/x-www-form-urlencoded; charset=UTF-8';G[_A8]=F
async with B.session.post(baseApi+E,data=A,headers=G)as J:B.session.cookie_jar.update_cookies(J.cookies);C=await J.json();C=b(C[_A],B.token);C=json.loads(C);return C async with B.session.post(baseApi+E,data=A,headers=G)as J:B.session.cookie_jar.update_cookies(J.cookies);C=await J.json();C=b(C[_A],B.token);C=json.loads(C);return C
elif E==get_web_token_api:A={O:'authorization_code',P:c(appKey+str(D)),K:appSecret,Q:S,R:F,I:appKey,_j:D,_H:A[_H]};H=a(json_dumps(A),F);A={_A:H+c(H+str(D)),_AH:d(F,B.publicKey),_j:str(D)} elif E==get_web_token_api:A={O:'authorization_code',P:c(appKey+str(D)),K:appSecret,Q:S,R:F,I:appKey,_f:D,_H:A[_H]};H=a(json_dumps(A),F);A={_A:H+c(H+str(D)),_AD:d(F,B.publicKey),_f:str(D)}
elif E==refresh_web_token_api:A={O:_AI,P:c(appKey+str(D)),K:appSecret,Q:S,R:F,I:appKey,_j:D,_AI:B.refreshToken};H=a(json_dumps(A),F);A={_A:H+c(H+str(D)),_AH:d(F,B.publicKey),_j:str(D)};E=get_web_token_api elif E==refresh_web_token_api:A={O:_AE,P:c(appKey+str(D)),K:appSecret,Q:S,R:F,I:appKey,_f:D,_AE:B.refreshToken};H=a(json_dumps(A),F);A={_A:H+c(H+str(D)),_AD:d(F,B.publicKey),_f:str(D)};E=get_web_token_api
else:A=B.encrypt_post_data(A) else:A=B.encrypt_post_data(A)
if L is not _F:G.update(L) if L is not _J:G.update(L)
if E in sessionIdControlApiList:G['sessionId']='web'+str(D) if E in sessionIdControlApiList:G['sessionId']='web'+str(D)
if E in keyCodeControlApiList:G[_AA]=F if E in keyCodeControlApiList:G[_A8]=F
if E in authControlApiList:G['Authorization']='Bearer '+B.accessToken[:len(B.accessToken)//2] if E in authControlApiList:G['Authorization']='Bearer '+B.accessToken[:len(B.accessToken)//2]
if E in tControlApiList:G['t']=B.token[:len(B.token)//2] if E in tControlApiList:G['t']=B.token[:len(B.token)//2]
async with B.session.post(baseApi+E,json=A,headers=G)as J: async with B.session.post(baseApi+E,json=A,headers=G)as J:
@@ -209,188 +174,158 @@ class StateGridDataClient:
if T in C:C=b(C[T],F);C=json.loads(C) if T in C:C=b(C[T],F);C=json.loads(C)
return C return C
async def __get_request_key(A): async def __get_request_key(A):
A.keyCode=_F;B=await A.__fetch(get_request_key_api,{});C=A.handle_request_result_message('get_request_key_api',B) A.keyCode=_J;B=await A.fetch(get_request_key_api,{});C=A.handle_request_result_message('get_request_key_api',B)
if B[_H]==_G:A.keyCode=B[_A][_AA];A.publicKey=B[_A][_AT];return{_C:0} if B[_H]==_F:A.keyCode=B[_A][_A8];A.publicKey=B[_A][_AO];return{_B:0}
return{_C:1,_f:C} return{_B:1,_e:C}
async def __get_qr_code(B): async def __get_qr_code(B):
C={_J:{_Y:'',_W:_w,_V:_M,_Z:''},_u:{'optType':_a,_N:e(28,10,1)}};A=await B.__fetch(get_qr_code_api,C);D=B.handle_request_result_message('get_qr_code_api',A) C={_K:{_Z:'',_X:_t,_W:_M,_a:''},_y:{'optType':_c,_N:e(28,10,1)}};A=await B.fetch(get_qr_code_api,C);D=B.handle_request_result_message('get_qr_code_api',A)
if A[_H]==1: if A[_H]==1:
if A[_A]and A[_A][_S]and A[_A][_S][_A1]==_AJ:B.qrCodeSerial=A[_A][_l][_A_];E=A[_A][_l]['qrCode'];return{_C:0,_A:E} if A[_A]and A[_A][_Q]and A[_A][_Q][_z]==_AF:B.qrCodeSerial=A[_A][_h][_Af];E=A[_A][_h]['qrCode'];return{_B:0,_A:E}
return{_C:1,_f:D} return{_B:1,_e:D}
async def __get_qr_code_status(B): async def __get_qr_code_status(B):
C={_l:{_A_:B.qrCodeSerial}};D={_k:'98'+e(10,10,1)};A=await B.__fetch(get_qr_code_status_api,C,D);E=B.handle_request_result_message('get_qr_code_status_api',A) C={_h:{_Af:B.qrCodeSerial}};D={_l:'98'+e(10,10,1)};A=await B.fetch(get_qr_code_status_api,C,D);E=B.handle_request_result_message('get_qr_code_status_api',A)
if _H in A and A[_H]==1: if _H in A and A[_H]==1:
if _A in A and A[_A]!='null':B.token=A[_A];return{_C:0} if _A in A and A[_A]!='null':B.token=A[_A];return{_B:0}
else:return{_C:1,_f:'未使用网上国网 App 扫码或确认登录'} else:return{_B:1,_e:'未使用网上国网 App 扫码或确认登录'}
return{_C:1,_f:E} return{_B:1,_e:E}
async def __get_qr_code_token(B): async def __get_qr_code_token(B):
C={_J:{_W:_w,_V:_M,'isEncrypt':_o},_k:B.token};A=await B.__fetch(get_qr_code_token_api,C);D=B.handle_request_result_message('get_qr_code_token_api',A) C={_K:{_X:_t,_W:_M,'isEncrypt':True},_l:B.token};A=await B.fetch(get_qr_code_token_api,C);D=B.handle_request_result_message('get_qr_code_token_api',A)
if _S in A and _A1 in A[_S]and A[_S][_A1]==_AJ:B.userInfo=A[_l][_AG];return{_C:0} if _Q in A and _z in A[_Q]and A[_Q][_z]==_AF:B.userInfo=A[_h][_AC];return{_B:0}
return{_C:1,_f:D} return{_B:1,_e:D}
async def __send_code(B,phone): async def __send_code(B,phone):
C=phone;B.phone=C;D={_J:{_Y:'',_W:_w,_V:_M,_Z:''},_u:{'sendType':'0',_A7:C,_B0:'login','accountType':''},'Channels':'web'};A=await B.__fetch(send_code_api,D);E=B.handle_request_result_message('send_code_api',A) C=phone;B.phone=C;D={_K:{_Z:'',_X:_t,_W:_M,_a:''},_y:{'sendType':'0',_n:C,_Ag:'login','accountType':''},'Channels':'web'};A=await B.fetch(send_code_api,D);E=B.handle_request_result_message('send_code_api',A)
if A[_H]==1: if A[_H]==1:
if A[_A]and A[_A][_S]and A[_A][_S][_A1]==_AJ:B.codeKey=A[_A][_l]['codeKey'];return{_C:0} if A[_A]and A[_A][_Q]and A[_A][_Q][_z]==_AF:B.codeKey=A[_A][_h]['codeKey'];return{_B:0}
return{_C:1,_f:E} return{_B:1,_e:E}
async def __verfiy_code(A,code): async def __verfiy_code(A,code):
C={_J:{_Y:'',_W:_w,_V:_M,_Z:''},_u:{_A7:A.phone,_B0:'login',_H:code,'optSys':'ios','pushId':'00000','codeKey':A.codeKey},'Channels':'web'};B=await A.__fetch(code_login_api,C);D=A.handle_request_result_message('code_login_api',B) C={_K:{_Z:'',_X:_t,_W:_M,_a:''},_y:{_n:A.phone,_Ag:'login',_H:code,'optSys':'ios','pushId':'00000','codeKey':A.codeKey},'Channels':'web'};B=await A.fetch(code_login_api,C);D=A.handle_request_result_message('code_login_api',B)
if _S in B and _A1 in B[_S]and B[_S][_A1]==_AJ:A.token=B[_l][_k];A.userInfo=B[_l][_AG][0];return{_C:0} if _Q in B and _z in B[_Q]and B[_Q][_z]==_AF:A.token=B[_h][_l];A.userInfo=B[_h][_AC][0];return{_B:0}
return{_C:1,_f:D} return{_B:1,_e:D}
async def __get_request_authorize(B): async def __get_request_authorize(B):
A=await B.__fetch(get_request_authorize_api,{});E=B.handle_request_result_message('get_request_authorize_api',A) A=await B.fetch(get_request_authorize_api,{});E=B.handle_request_result_message('get_request_authorize_api',A)
if _H in A and A[_H]==_G:C=A[_A][_Az];D=C.rfind('code=');B.authorizeCode=C[D+5:D+5+32];return{_C:0} if _H in A and A[_H]==_F:C=A[_A][_Ae];D=C.rfind('code=');B.authorizeCode=C[D+5:D+5+32];return{_B:0}
return{_C:1,_f:E} return{_B:1,_e:E}
async def __get_web_token(A): async def __get_web_token(A):
C={_H:A.authorizeCode};B=await A.__fetch(get_web_token_api,C);D=A.handle_request_result_message('get_web_token_api',B) C={_H:A.authorizeCode};B=await A.fetch(get_web_token_api,C);D=A.handle_request_result_message('get_web_token_api',B)
if _H in B and B[_H]==_G:A.accessToken=B[_A][_B1];A.refreshToken=B[_A][_AI];return{_C:0} if _H in B and B[_H]==_F:A.accessToken=B[_A][_Ah];A.refreshToken=B[_A][_AE];return{_B:0}
return{_C:1,_f:D} return{_B:1,_e:D}
async def __refresh_web_token(B): async def __refresh_web_token(B):
A=await B.__fetch(refresh_web_token_api,{});C=B.handle_request_result_message('refresh_web_token_api',A) A=await B.fetch(refresh_web_token_api,{});C=B.handle_request_result_message('refresh_web_token_api',A)
if _H in A and A[_H]==_G:B.accessToken=A[_A][_B1];B.refreshToken=A[_A][_AI];return{_C:0} if _H in A and A[_H]==_F:B.accessToken=A[_A][_Ah];B.refreshToken=A[_A][_AE];return{_B:0}
return{_C:1,_f:C} return{_B:1,_e:C}
async def __get_door_number(A): async def __get_door_number(B):
B=configuration[_Al];G={_B:B[_B],_E:B[_E],_P:B[_P],_J:{_V:B[_J][_V],_Y:B[_J][_Y],_Z:B[_J][_Z],_W:B[_J][_W]},_u:{_R:A.userInfo[_R]},_k:A.token};C=await A.__fetch_safe(get_door_number_api,G);H=A.handle_request_result_message('get_door_number_api',C) C={_C:configuration[_C],_E:configuration[_E],_S:configuration[_S],_K:{_W:configuration[_A5][_K][_W],_Z:configuration[_A5][_K][_Z],_a:configuration[_A5][_K][_a],_X:configuration[_A5][_K][_X]},_y:{_R:B.userInfo[_R]},_l:B.token};A=await B.fetch(get_door_number_api,C);D=B.handle_request_result_message('get_door_number_api',A)
if _H in C and C[_H]==1 and _A in C and _l in C[_A]: if _H in A and A[_H]==1 and _A in A and _h in A[_A]:B.powerUserList=A[_A][_h][_AP];return{_B:0}
E={} return{_B:1,_e:D}
if A.powerUserList is not _F:E={A[_g]:A for A in A.powerUserList} async def __get_door_balance(C,door_account):
F=[] A=door_account;E={_A:{_L:'',_N:'',_G:configuration[_n][_G],_D:configuration[_n][_D],_A0:C.userInfo[_R],_A1:C.userInfo[_A9],_T:_F,_d:_F,_AG:C.userInfo[_R],_A2:[{_Ai:A[_m],_AA:A[_U],_Aj:A[_AQ],_i:A[_i],_Y:A[_Y]}]},_C:_AK,_E:configuration[_E],_S:A[_U]};B=await C.fetch(get_door_balance_api,E);C.handle_request_result_message('get_door_balance_api',B)
for D in C[_A][_l][_AU]: if _H in B and B[_H]==1 and _A in B and _A2 in B[_A]:
if D[_g]in E:F.append(E[D[_g]]) D=B[_A][_A2]
elif _AV in D and D[_AV]!='05':F.append(D) if len(D)!=0:A[_A3]=D[0]
A.powerUserList=F;return{_C:0} async def __get_door_bill(C,door_account,year):
return{_C:1,_f:H} F='dataInfo';D='mothEleList';A=door_account;E={_A:{_L:'',_N:'',_G:configuration[_n][_G],_D:configuration[_n][_D],_A0:C.userInfo[_R],_A1:C.userInfo[_A9],_T:_F,_d:_F,_AG:C.userInfo[_R],_A2:[{_Ai:A[_m],_AA:A[_U],_Aj:A['consSortCode'],_i:A[_i],_Y:A[_Y]}]},_C:_AK,_E:configuration[_E],_S:A[_U]};E={_A:{_A0:C.userInfo[_R],_G:configuration[_G],_g:'11',_AR:A[_AQ],_D:'ALIPAY_01',_Y:A[_Y],_AA:A[_U],_d:_F,_T:_F,_N:'',_L:'',_A1:'',_Ak:A[_U],_AG:C.userInfo[_R],_i:A[_i],_Al:year},_C:_x,_E:_w,_S:A[_U]};B=await C.fetch(get_door_bill_api,E);C.handle_request_result_message('get_door_bill_api',B)
async def __get_door_balance(B,door_account):
A=door_account;E={_A:{_L:'',_N:'',_I:configuration[_A7][_I],_D:configuration[_A7][_D],_A2:B.userInfo[_R],_A3:B.userInfo.get(_AC,B.userInfo.get('nickname',_F)),_U:_G,_b:_G,_AW:B.userInfo[_R],_AB:[{'consNoSrc':A[_g],_AD:A.get(_X,A.get(_A4,_F)),'sceneType':A.get('consSortCode',A.get(_AV,_F)),_p:A[_p],_c:A[_c]}]},_B:'0101143',_E:configuration[_E],_P:A.get(_X,A.get(_A4,_F))};C=await B.__fetch_safe(get_door_balance_api,E);B.handle_request_result_message('get_door_balance_api',C)
if _H in C and C[_H]==1 and _A in C and _AB in C[_A]:
D=C[_A][_AB]
if len(D)!=0:A[_A5]=D[0]
async def __get_door_bill(C,door_account,monthDate):
I='dataInfo';G='mothEleList';E=monthDate;A=door_account;J={_A:{_A2:C.userInfo[_R],_I:configuration[_I],_e:'11',_AK:A[_B3],_D:'ALIPAY_01',_c:A[_c],_AD:A[_X],_b:_G,_U:_G,_N:'',_L:'',_A3:'',_B2:A[_X],_AW:C.userInfo[_R],_p:A[_p],_AX:E.year},_B:_A0,_E:_z,_P:A[_X]};B=await C.__fetch_safe(get_door_bill_api,J);C.handle_request_result_message('get_door_bill_api',B)
if _H in B and B[_H]==1 and _A in B: if _H in B and B[_H]==1 and _A in B:
if I in B[_A]:A[_AL]=B[_A][I] if F in B[_A]:A[_AH]=B[_A][F]
if G in B[_A]: if D in B[_A]:A[_A4]=B[_A][D];A[_AI]=B[_A][D][-1]['month']
if _AE not in A or A[_AE]!=E.year:A[_m]=B[_A][G];A[_AE]=E.year async def __get_door_ladder(B,door_account,month):
else: F='ladder_flag';E=month;A=door_account;G=A[_m];H={_A:{_G:configuration[_o][_G],_D:configuration[_o][_D],_T:configuration[_o][_T],_g:configuration[_o][_g],_i:A[_m],_d:A[_U],_Y:A[_Y],'queryDate':E,_Ak:A[_U],_AR:A[_AQ],_AG:B.userInfo[_R],_N:'',_L:'',_A1:B.userInfo[_A9],_A0:B.userInfo[_R]},_C:configuration[_o][_C],_E:configuration[_o][_E],_S:A[_U]};C=await B.fetch(get_door_ladder_api,H);I=B.handle_request_result_message('get_door_ladder_api',C)
F={A.month:A for A in A[_m]};H=B[_A][G] if _H in C and C[_H]==1 and _A in C and _A2 in C[_A]:
for D in H: D=C[_A][_A2]
if D.month in F and _h in F[D.month]:D[_h]=F[D.month][_h] if len(D)!=0:
A[_m]=H D=D[0];D['month']=E
if len(A[_m])>0:A[_v]=A[_m][-1] if D['electricParticulars']['levelFlag']=='2':A[F]=1
async def __get_door_ladder(C,door_account,monthBill): else:A[F]=0
E=monthBill;A=door_account;I=A[_g];F=datetime.datetime.strptime(E[_AM],'%Y%m');G=f"{F.year}-{F.month:02d}";H={_A:{_I:configuration[_q][_I],_D:configuration[_q][_D],_U:configuration[_q][_U],_e:configuration[_q][_e],_p:A[_g],_b:A[_X],_c:A[_c],'queryDate':G,_B2:A[_X],_AK:A[_B3],_AW:C.userInfo[_R],_N:'',_L:'',_A3:C.userInfo[_AC],_A2:C.userInfo[_R]},_B:configuration[_q][_B],_E:configuration[_q][_E],_P:A[_X]};B=await C.__fetch(get_door_ladder_api,H);J=C.handle_request_result_message('get_door_ladder_api',B) B.doorAccountDict[G]['ladder']=D
if _H in B and B[_H]==1 and _A in B and _AB in B[_A]: async def __get_door_daily_bill(A,door_account,year,start_date,end_date):
D=B[_A][_AB] D='sevenEleList';C=door_account;E={'params1':{_C:configuration[_C],_E:configuration[_E],_S:configuration[_S],_K:{_W:configuration[_K][_W],_Z:configuration[_K][_Z],_a:configuration[_K][_a],_X:configuration[_K][_X]},_y:{_R:A.userInfo[_R]},_l:A.token},'params3':{_A:{_A0:A.userInfo[_R],_i:C[_m],_AR:_c,'endTime':end_date,_Y:C[_Y],_Al:year,_AA:C[_U],_N:'',_L:'','startTime':start_date,_A1:A.userInfo[_A9],_D:configuration[_j][_D],_G:configuration[_j][_G],_g:configuration[_j][_g],_d:configuration[_j][_d],_T:configuration[_j][_T]},_C:configuration[_j][_C],_E:configuration[_j][_E],_S:C[_U]},'params4':'010103'};B=await A.fetch(get_door_daily_bill_api,E);F=A.handle_request_result_message('get_door_daily_bill_api',B)
if len(D)!=0:D=D[0];E['ladder']=D if _H in B and B[_H]==1 and _A in B and D in B[_A]:C[_V]=B[_A][D]
async def __get_door_mouth_daily_bill(B,door_account,monthBill): async def __get_door_pay_record(A,door_account):B=door_account;D=B[_m];C={'params1':{_C:configuration[_C],_E:configuration[_E],_S:configuration[_S],_K:{_W:configuration[_K][_W],_Z:configuration[_K][_Z],_a:configuration[_K][_a],_X:configuration[_K][_X]},_y:{_R:A.userInfo[_R]},_l:A.token},'params3':{_A:{_A0:A.userInfo[_R],'bgnPayDate':'2023-04-24',_G:configuration[_G],_i:B[_m],'endPayDate':'2024-04-24',_D:'webALIPAY_01','number':100,_Y:B[_Y],'page':_F,_AA:B[_U],_d:_F,_T:_F,_N:'',_L:'',_A1:A.userInfo[_A9]},_C:'0101051',_E:_c,_S:B[_U]},'params4':'010104'};E=await A.fetch(getJiaoFeiRecordApi,C)
I=monthBill;C=door_account;A=datetime.datetime.strptime(I[_AM],'%Y%m');H=datetime.date(A.year,A.month,1)
if A.month==12:D=datetime.date(A.year+1,1,1)
else:D=datetime.date(A.year,A.month+1,1)
D=D-datetime.timedelta(days=1);O=f"{H.year}-{H.month:02d}-{H.day:02d}";P=f"{D.year}-{D.month:02d}-{D.day:02d}";Q={_AY:{_B:configuration[_B],_E:configuration[_E],_P:configuration[_P],_J:{_V:configuration[_J][_V],_Y:configuration[_J][_Y],_Z:configuration[_J][_Z],_W:configuration[_J][_W]},_u:{_R:B.userInfo[_R]},_k:B.token},_AZ:{_A:{_A2:B.userInfo[_R],_p:C[_g],_AK:_a,'endTime':P,_c:C[_c],_AX:str(A.year),_AD:C.get(_X,C.get(_A4,_F)),_N:'',_L:'',_B4:O,_A3:B.userInfo[_AC],_D:configuration[_T][_D],_I:configuration[_T][_I],_e:configuration[_T][_e],_b:configuration[_T][_b],_U:configuration[_T][_U]},_B:configuration[_T][_B],_E:configuration[_T][_E],_P:C.get(_X,C.get(_A4,_F))},_Aa:'010103'};E=await B.__fetch_safe(get_door_daily_bill_api,Q);R=B.handle_request_result_message(_B5,E)
if _H in E and E[_H]==1 and _A in E and _AN in E[_A]:
J=0;K=0;L=0;M=0;N=0
for F in E[_A][_AN]:S=datetime.datetime.strptime(F['day'],_Ab);J+=catchFloat(F,_AO);K+=catchFloat(F,_Ac);L+=catchFloat(F,_Ad);M+=catchFloat(F,_Ae);N+=catchFloat(F,_Af)
G={};G[_Ag]=normal_round(J,2);G[_Ah]=normal_round(K,2);G[_Ai]=normal_round(L,2);G[_Aj]=normal_round(M,2);G[_Ak]=normal_round(N,2);I[_h]=G
async def __get_door_daily_bill(B,door_account,year,start_date,end_date):
A=door_account;D={_AY:{_B:configuration[_B],_E:configuration[_E],_P:configuration[_P],_J:{_V:configuration[_J][_V],_Y:configuration[_J][_Y],_Z:configuration[_J][_Z],_W:configuration[_J][_W]},_u:{_R:B.userInfo[_R]},_k:B.token},_AZ:{_A:{_A2:B.userInfo[_R],_p:A[_g],_AK:_a,'endTime':end_date,_c:A[_c],_AX:year,_AD:A.get(_X,A.get(_A4,_F)),_N:'',_L:'',_B4:start_date,_A3:B.userInfo[_AC],_D:configuration[_T][_D],_I:configuration[_T][_I],_e:configuration[_T][_e],_b:configuration[_T][_b],_U:configuration[_T][_U]},_B:configuration[_T][_B],_E:configuration[_T][_E],_P:A.get(_X,A.get(_A4,_F))},_Aa:'010103'};C=await B.__fetch_safe(get_door_daily_bill_api,D);E=B.handle_request_result_message(_B5,C)
if _H in C and C[_H]==1 and _A in C and _AN in C[_A]:A[_A6]=C[_A][_AN]
async def __get_door_pay_record(A,door_account):B=door_account;D=B[_g];C={_AY:{_B:configuration[_B],_E:configuration[_E],_P:configuration[_P],_J:{_V:configuration[_J][_V],_Y:configuration[_J][_Y],_Z:configuration[_J][_Z],_W:configuration[_J][_W]},_u:{_R:A.userInfo[_R]},_k:A.token},_AZ:{_A:{_A2:A.userInfo[_R],'bgnPayDate':'2023-04-24',_I:configuration[_I],_p:B[_g],'endPayDate':'2024-04-24',_D:'webALIPAY_01','number':100,_c:B[_c],'page':_G,_AD:B[_X],_b:_G,_U:_G,_N:'',_L:'',_A3:A.userInfo[_AC]},_B:'0101051',_E:_a,_P:B[_X]},_Aa:'010104'};E=await A.__fetch(getJiaoFeiRecordApi,C)
async def get_qr_code(B): async def get_qr_code(B):
A=await B.__get_request_key() A=await B.__get_request_key()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
return await B.__get_qr_code() return await B.__get_qr_code()
async def check_qr_code(B): async def check_qr_code(B):
A=await B.__get_qr_code_status() A=await B.__get_qr_code_status()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
A=await B.__get_qr_code_token() A=await B.__get_qr_code_token()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
return await B.__get_token() return await B.__get_token()
async def send_phone_code(B,phone): async def send_phone_code(B,phone):
A=await B.__get_request_key() A=await B.__get_request_key()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
return await B.__send_code(phone) return await B.__send_code(phone)
async def verfiy_phone_code(B,code): async def verfiy_phone_code(B,code):
A=await B.__verfiy_code(code) A=await B.__verfiy_code(code)
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
return await B.__get_token() return await B.__get_token()
async def __get_token(B): async def __get_token(B):
A=await B.__get_request_key() A=await B.__get_request_key()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
A=await B.__get_request_authorize() A=await B.__get_request_authorize()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
A=await B.__get_web_token() A=await B.__get_web_token()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
A=await B.__get_door_number() A=await B.__get_door_number()
if _C in A and A[_C]!=0:return A if _B in A and A[_B]!=0:return A
B.need_login=_i;await B.save_data();return{_C:0,_A:B.powerUserList} B.need_login=_s;await B.save_data();return{_B:0,_A:B.powerUserList}
async def __refresh_token(A): async def refresh_data(B,setup=_s):
B=await A.__get_request_key() n='daily_t_ele_num';m='daily_n_ele_num';l='daily_v_ele_num';k='daily_p_ele_num';j='daily_ele_num';i='last_month_ele_cost';h='last_month_ele_num';g='year_ele_cost';f='year_ele_num';e='monthEleNum';d='thisTPq';c='thisNPq';b='thisVPq';a='thisPPq';R='ladder_level_num';Q='ladder_level';L='balance';K='%Y%m%d';J='day';H='dayElePq'
if _C in B and B[_C]!=0:return if B.need_login is True:LOGGER.error('国家电网需要重新登录!');return
B=await A.__refresh_web_token() o=setup or int(time.time()*1000)-B.timestamp>B.refresh_interval*3600*1000
if _C in B and B[_C]==0:A.need_login=_i;A.retry_times=0;await A.save_data() if o is _s:return
elif A.retry_times>=3:A.need_login=_o D=await B.__get_door_number()
else:A.retry_times=A.retry_times+1 if _B in D and D[_B]!=0:
async def refresh_data(B,setup=_i,force_refresh=_i): LOGGER.warning('刷新 Token');D=await B.__get_request_key()
p='last_month_ele_cost';o='year_ele_cost';n='%Y-%m-%d %H:%M:%S';b=setup;a='last_month_ele_num';Z='daily_lasted_date';Y='refresh_time';U='year_ele_num';G='balance' if _B in D and D[_B]!=0:return
if b is _o: D=await B.__refresh_web_token()
if B.dataVersion!=VERSION:B.powerUserList=_F if _B in D and D[_B]==0:await B.save_data()
c=await B.__get_door_number() else:B.need_login=True;LOGGER.error('刷新 Token 失败');return
if _C in c and c[_C]!=0:B.need_login=_o D=await B.__get_door_number()
if B.need_login is _o: if _B in D and D[_B]!=0:B.need_login=True;LOGGER.error('重新请求失败');return
if B.powerUserList is not _F: M=datetime.datetime.now();I=M-datetime.timedelta(days=1);p=f"{I.year}-{I.month:02d}-{I.day:02d}";N=I-datetime.timedelta(days=40);q=f"{N.year}-{N.month:02d}-{N.day:02d}"
for A in B.get_door_account_list():A[Y]='Token刷新失败,请重新登录'
LOGGER.error('国家电网 - Token刷新失败,请重新登录!');return
q=b or force_refresh or int(time.time()*1000)-B.timestamp>B.refresh_interval*3600*1000
if q is _i:return
H=datetime.datetime.now();F=H-datetime.timedelta(days=1);d=f"{F.year}-{F.month:02d}-{F.day:02d}";V=F-datetime.timedelta(days=40);r=f"{V.year}-{V.month:02d}-{V.day:02d}"
for A in B.powerUserList: for A in B.powerUserList:
s=A[_g];B.doorAccountDict[s]=A r=A[_m];B.doorAccountDict[r]=A;await B.__get_door_balance(A);await B.__get_door_daily_bill(A,M.year,q,p);O=A[_V][0]
if Z in A and A[Z]==d:A[Y]=datetime.datetime.strftime(H,n);continue try:float(O[H])
await B.__get_door_balance(A) except:A[_V].pop(0)
if B.retry_times!=0:return O=A[_V][0];E=datetime.datetime.strptime(O[J],K);S=0;T=0;U=0;V=0;W=0
if _A5 in A: for C in A[_V]:
t=catchFloat(A[_A5],'estiAmt');W=catchFloat(A[_A5],'prepayBal');e=catchFloat(A[_A5],'sumMoney') G=datetime.datetime.strptime(C[J],K)
if W==0 or W==e:A[G]=e if G.month!=E.month:break
else:A[G]=W-t S+=catchFloat(C,H);T+=catchFloat(C,a);U+=catchFloat(C,b);V+=catchFloat(C,c);W+=catchFloat(C,d)
f=catchFloat(A[_A5],'historyOwe') P=E-datetime.timedelta(days=E.day);X=f"{P.year}-{P.month:02d}"
if f>0:A[G]=-f if _AI not in A or A[_AI]!=X:await B.__get_door_bill(A,P.year);await B.__get_door_ladder(A,X)
else:LOGGER.error('国家电网账户余额获取失败!') s=datetime.datetime.strptime(A[_AI],'%Y%m')
if G not in A:A[G]=0 if s.month==12:
await B.__get_door_daily_bill(A,H.year,r,d) F=0
if _A6 not in A:LOGGER.error('国家电网无法获取日用电数据!');continue for C in A[_V]:
X=0;I=_i G=datetime.datetime.strptime(C[J],K)
for g in range(10): if G.month!=12:break
D=A[_A6][g] F+=catchFloat(C,H)
try:float(D[_AO]);I=_o;break
except:X=X+1
h=0;i=0;j=0;k=0;l=0
if I:
for g in range(X):A[_A6].pop(0)
D=A[_A6][0];E=datetime.datetime.strptime(D['day'],_Ab);A[Z]=f"{E.year}-{E.month:02d}-{E.day:02d}";h=catchFloat(D,_AO);i=catchFloat(D,_Ac);j=catchFloat(D,_Ad);k=catchFloat(D,_Ae);l=catchFloat(D,_Af)
A['daily_ele_num']=normal_round(h,2);A['daily_p_ele_num']=normal_round(i,2);A['daily_v_ele_num']=normal_round(j,2);A['daily_n_ele_num']=normal_round(k,2);A['daily_t_ele_num']=normal_round(l,2);J=0;K=0;L=0;M=0;N=0
if I:
for C in A[_A6]:
u=datetime.datetime.strptime(C['day'],_Ab)
if u.month!=E.month:break
J+=catchFloat(C,_AO);K+=catchFloat(C,_Ac);L+=catchFloat(C,_Ad);M+=catchFloat(C,_Ae);N+=catchFloat(C,_Af)
A[_Ag]=normal_round(J,2);A[_Ah]=normal_round(K,2);A[_Ai]=normal_round(L,2);A[_Aj]=normal_round(M,2);A[_Ak]=normal_round(N,2)
if I:
O=E-datetime.timedelta(days=E.day);v=f"{O.year}{O.month:02d}"
if _AE not in A or A[_AE]!=O.year or _v not in A or A[_v][_AM]!=v:await B.__get_door_bill(A,O)
if _m in A:
for C in A[_m]:
if _h not in C:await B.__get_door_mouth_daily_bill(A,C)
if _AL in A:A[U]=catchFloat(A[_AL],'totalEleNum');A[o]=catchFloat(A[_AL],'totalEleCost')
if U not in A:A[U]=0;A[o]=0
if _v in A:A[a]=catchFloat(A[_v],'monthEleNum');A[p]=catchFloat(A[_v],'monthEleCost');m=datetime.datetime.strptime(A[_v][_AM],'%Y%m')
if a not in A:A[a]=0;A[p]=0;m=F.month
P=0;Q=0;R=0;S=0;T=0
if m.month==12:P=J;Q=K;R=L;S=M;T=N
else: else:
if _m in A: F=0
for C in A[_m]: for C in A[_A4]:F+=catchFloat(C,e)
if _h in C:P+=C[_h][_Ag];Q+=C[_h][_Ah];R+=C[_h][_Ai];S+=C[_h][_Aj];T+=C[_h][_Ak] t=len(A[_A4])
P+=J;Q+=K;R+=L;S+=M;T+=N for C in A[_V]:
A[U]=normal_round(P,2);A['year_p_ele_num']=normal_round(Q,2);A['year_v_ele_num']=normal_round(R,2);A['year_n_ele_num']=normal_round(S,2);A['year_t_ele_num']=normal_round(T,2);A[Y]=datetime.datetime.strftime(H,n) G=datetime.datetime.strptime(C[J],K)
if G.month<=t:break
F+=catchFloat(C,H)
if F<=2760:A[Q]='第一阶梯';A[R]=1
elif F<=4800:A[Q]='第二阶梯';A[R]=2
else:A[Q]='第三阶梯';A[R]=3
if _A3 in A:
u=catchFloat(A[_A3],'estiAmt');Y=catchFloat(A[_A3],'prepayBal');v=catchFloat(A[_A3],'sumMoney')
if Y==0:A[L]=v
else:A[L]=Y-u
Z=catchFloat(A[_A3],'historyOwe')
if Z>0:A[L]=-Z
else:A[L]=0
if _AH in A:A[f]=catchFloat(A[_AH],'totalEleNum');A[g]=catchFloat(A[_AH],'totalEleCost')
else:A[f]=0;A[g]=0
if _A4 in A:A[h]=catchFloat(A[_A4][-1],e);A[i]=catchFloat(A[_A4][-1],'monthEleCost')
else:A[h]=0;A[i]=0
if _V in A:A[j]=catchFloat(A[_V][0],H);A[k]=catchFloat(A[_V][0],a);A[l]=catchFloat(A[_V][0],b);A[m]=catchFloat(A[_V][0],c);A[n]=catchFloat(A[_V][0],d)
else:A[j]=0;A[k]=0;A[l]=0;A[m]=0;A[n]=0
A['month_ele_num']=S;A['month_p_ele_num']=T;A['month_v_ele_num']=U;A['month_n_ele_num']=V;A['month_t_ele_num']=W;A['daily_lasted_date']=f"{E.year}-{E.month:02d}-{E.day:02d}";A['refresh_time']=datetime.datetime.strftime(M,'%Y-%m-%d %H:%M:%S')
await B.save_data() await B.save_data()
def get_door_account_list(A):return list(A.doorAccountDict.values()) async def get_door_account_list(A):return list(A.doorAccountDict.values())
def get_door_account(A):return A.doorAccountDict def get_door_account(A):return A.doorAccountDict
+2 -2
View File
@@ -3,7 +3,7 @@
"name": "国家电网", "name": "国家电网",
"codeowners": ["@hassbox"], "codeowners": ["@hassbox"],
"config_flow": true, "config_flow": true,
"documentation": "https://hassbox.cn/", "documentation": "https://github.com/hass-box/state-grid",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"version": "0.1.4" "version": "0.0.5"
} }
+139 -175
View File
@@ -2,11 +2,13 @@ from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN, DOMAIN as SENSOR_DOMAIN,
SensorDeviceClass, SensorDeviceClass,
SensorEntity, SensorEntity,
SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfEnergy from homeassistant.const import UnitOfEnergy
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -16,213 +18,175 @@ from .const import DOMAIN, VERSION
from .data_client import StateGridDataClient from .data_client import StateGridDataClient
from .coordinator import StateGridCoordinator from .coordinator import StateGridCoordinator
UNIT_YUAN = "" CURRENCY_YUAN = ""
ENTITY_ID_SENSOR_FORMAT = SENSOR_DOMAIN + ".state_grid_" ENTITY_ID_SENSOR_FORMAT = SENSOR_DOMAIN + ".state_grid_"
SENSOR_TYPES = [ SENSOR_TYPES = (
{ SensorEntityDescription(
"key":"balance", key="balance",
"name": "账户余额", translation_key="balance",
"native_unit_of_measurement": UNIT_YUAN, native_unit_of_measurement=CURRENCY_YUAN,
"device_class": SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.MONETARY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "year_ele_num", key="year_ele_num",
"name": "年度累计用电", translation_key="year_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "year_p_ele_num", key="year_ele_cost",
"name": "年度累计峰用电", translation_key="year_ele_cost",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=CURRENCY_YUAN,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.MONETARY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "year_v_ele_num", key="last_month_ele_num",
"name": "年度累计谷用电", translation_key="last_month_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "year_n_ele_num", key="last_month_ele_cost",
"name": "年度累计平用电", translation_key="last_month_ele_cost",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=CURRENCY_YUAN,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.MONETARY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "year_t_ele_num", key="month_ele_num",
"name": "年度累计尖用电", translation_key="month_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "year_ele_cost", key="month_p_ele_num",
"name": "年度累计电费", translation_key="month_p_ele_num",
"native_unit_of_measurement": UNIT_YUAN, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "last_month_ele_num", key="month_v_ele_num",
"name": "上个月用电", translation_key="month_v_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "last_month_ele_cost", key="month_n_ele_num",
"name": "上个月电费", translation_key="month_n_ele_num",
"native_unit_of_measurement": UNIT_YUAN, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "month_ele_num", key="month_t_ele_num",
"name": "当月累计用电", translation_key="month_t_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "month_p_ele_num", key="daily_ele_num",
"name": "当月累计峰用电", translation_key="daily_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "month_v_ele_num", key="daily_p_ele_num",
"name": "当月累计谷用电", translation_key="daily_p_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "month_n_ele_num", key="daily_v_ele_num",
"name": "当月累计平用电", translation_key="daily_v_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "month_t_ele_num", key="daily_n_ele_num",
"name": "当月累计尖用电", translation_key="daily_n_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "daily_ele_num", key="daily_t_ele_num",
"name": "日总用电", translation_key="daily_t_ele_num",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL state_class=SensorStateClass.TOTAL
}, ),
{ SensorEntityDescription(
"key": "daily_p_ele_num", key="daily_lasted_date",
"name": "日峰用电", translation_key="daily_lasted_date"
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, ),
"device_class": SensorDeviceClass.ENERGY, SensorEntityDescription(
"state_class": SensorStateClass.TOTAL key="refresh_time",
}, translation_key="refresh_time"
{ )
"key": "daily_v_ele_num", )
"name": "日谷用电",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL
},
{
"key": "daily_n_ele_num",
"name": "日平用电",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL
},
{
"key": "daily_t_ele_num",
"name": "日尖用电",
"native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL
},
{
"key": "daily_lasted_date",
"name": "最新日用电日期"
},
{
"key": "refresh_time",
"name": "最近刷新时间"
}
]
SENSOR_TYPES_FOR_LADDER = [ SENSOR_TYPES_FOR_LADDER = (
{ SensorEntityDescription(
"key": "ladder_level", key="ladder_level",
"name": "当前阶梯" translation_key="ladder_level"
}, ),
] )
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
data_client: StateGridDataClient = hass.data[DOMAIN]
coordinator = StateGridCoordinator(hass) coordinator = StateGridCoordinator(hass)
data_client.coordinator = coordinator
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
door_account_list = data_client.get_door_account_list() data_client: StateGridDataClient = hass.data[DOMAIN]
door_account_list = await data_client.get_door_account_list()
for door_account in door_account_list: for door_account in door_account_list:
async_add_entities( async_add_entities(
[StateGridSensor(door_account, sensor_type, entry.entry_id, coordinator) for sensor_type in SENSOR_TYPES] [StateGridSensor(door_account, description, entry.entry_id, coordinator) for description in SENSOR_TYPES]
) )
# if door_account["ladder_flag"] == 1: # if door_account["ladder_flag"] == 1:
# async_add_entities( # async_add_entities(
# StateGridSensor(door_account, sensor_type, entry.entry_id) # StateGridSensor(door_account, description, entry.entry_id)
# for sensor_type in SENSOR_TYPES_FOR_LADDER # for description in SENSOR_TYPES_FOR_LADDER
# ) # )
class StateGridSensor(CoordinatorEntity[StateGridCoordinator], SensorEntity): class StateGridSensor(CoordinatorEntity[StateGridCoordinator], SensorEntity):
_attr_has_entity_name = True _attr_has_entity_name = True
entity_description: SensorEntityDescription
def __init__( def __init__(
self, door_account, sensor_type, entry_id: str, coordinator: StateGridCoordinator, self, door_account, entity_description: SensorEntityDescription, entry_id: str, coordinator: StateGridCoordinator,
) -> None: ) -> None:
super().__init__(coordinator) super().__init__(coordinator)
self.entity_description = entity_description
self.door_account = door_account self.door_account = door_account
self.sensor_type = sensor_type self.entity_id = SENSOR_DOMAIN + ".state_grid" + "_" + door_account["consNo_dst"] + "_" + entity_description.key
self.entity_id = SENSOR_DOMAIN + ".state_grid" + "_" + door_account["consNo_dst"] + "_" + sensor_type["key"] self._attr_translation_key = entity_description.key
self._attr_name = sensor_type["name"] self._attr_unique_id = entry_id + "-" + door_account["consNo_dst"] + "-" + entity_description.key
self._attr_unique_id = entry_id + "-" + door_account["consNo_dst"] + "-" + sensor_type["key"]
if "device_class" in sensor_type:
self._attr_device_class = sensor_type["device_class"]
if "state_class" in sensor_type:
self._attr_state_class = sensor_type["state_class"]
if "native_unit_of_measurement" in sensor_type:
self._attr_native_unit_of_measurement = sensor_type["native_unit_of_measurement"]
self._attr_device_info = { self._attr_device_info = {
"name": door_account["elecAddr_dst"], "name": door_account["elecAddr_dst"],
"identifiers": {(DOMAIN, door_account["consNo_dst"])}, "identifiers": {(DOMAIN, door_account["consNo_dst"])},
"sw_version": VERSION, "sw_version": VERSION,
"manufacturer": "HassBox", "manufacturer": "HassBox",
"model": "户号:" + door_account["consName_dst"] + " - " + door_account["consNo_dst"] "model": "户号:" + door_account["consName_dst"] + " - " + door_account["consNo_dst"],
"entry_type": DeviceEntryType.SERVICE,
} }
@property @property
def native_value(self): def native_value(self):
data = self.coordinator.data[self.door_account["consNo_dst"]] data = self.coordinator.data[self.door_account["consNo_dst"]]
return data[self.sensor_type["key"]] return data[self.entity_description.key]
+58
View File
@@ -70,5 +70,63 @@
"get_qr_code_error": "{errmsg}", "get_qr_code_error": "{errmsg}",
"check_qr_code_error": "{errmsg}" "check_qr_code_error": "{errmsg}"
} }
},
"entity": {
"sensor": {
"balance": {
"name": "账户余额"
},
"year_ele_num": {
"name": "年度累计用电"
},
"year_ele_cost": {
"name": "年度累计电费"
},
"last_month_ele_num": {
"name": "上个月用电"
},
"last_month_ele_cost": {
"name": "上个月电费"
},
"month_ele_num": {
"name": "当月累计用电"
},
"month_p_ele_num": {
"name": "当月累计峰用电"
},
"month_v_ele_num": {
"name": "当月累计谷用电"
},
"month_n_ele_num": {
"name": "当月累计平用电"
},
"month_t_ele_num": {
"name": "当月累计尖用电"
},
"daily_ele_num": {
"name": "日总用电"
},
"daily_p_ele_num": {
"name": "日峰用电"
},
"daily_v_ele_num": {
"name": "日谷用电"
},
"daily_n_ele_num": {
"name": "日平用电"
},
"daily_t_ele_num": {
"name": "日尖用电"
},
"daily_lasted_date": {
"name": "最新日用电日期"
},
"ladder_level": {
"name": "当前阶梯"
},
"refresh_time": {
"name": "最近刷新时间"
}
}
} }
} }
@@ -1,74 +0,0 @@
{
"config": {
"flow_title": "国家电网",
"step": {
"user": {
"title": "国家电网",
"description": "集成国家电网可以选择网上国网 App 扫码登录或短信验证码登录。如登录异常,可至 **HassBox全屋智能** 微信公众号联系客服。"
},
"code_login": {
"title": "短信验证码登录",
"description": "手机号需先在网上电网 App 注册并绑定户号",
"data": {
"phone": "请输入手机号"
}
},
"verfiy_code": {
"title": "短信验证码登录",
"data": {
"code": "请输入验证码"
}
},
"check_qr_code": {
"title": "网上国网 App 扫码登录",
"description": "\n {qr_image} \n\n\n 请使用 网上国网 App 扫码登录,确认登录后再点击提交。"
}
},
"abort": {
"single_instance_allowed": "该集成已经配置过了,且只能配置一次。若要重新配置,请先删除旧集成。",
"get_qr_code_error": "{errmsg}",
"check_qr_code_error": "{errmsg}"
}
},
"options": {
"step": {
"debug": {
"title": "国家电网",
"description": "国家电网当日用电等数据一般隔天才会刷新,且各地区时间不一,可根据情况适当调整接口轮询间隔。",
"data": {
"refresh_interval": "轮询间隔",
"is_debug": "调试模式"
},
"data_description": {
"is_debug": "打开调试开关后,将会打印所有请求日志。可在[配置]-[系统]-[日志]中查看。\n如需反馈异常,可至 **HassBox全屋智能** 微信公众号联系客服。"
}
},
"user": {
"title": "重新登录国家电网",
"description": "刷新 Token 异常,需重新登录。\n如需反馈异常,可至 **HassBox全屋智能** 微信公众号联系客服。"
},
"code_login": {
"title": "短信验证码登录",
"description": "手机号需先在网上电网 App 注册并绑定户号",
"data": {
"phone": "请输入手机号"
}
},
"verfiy_code": {
"title": "短信验证码登录",
"data": {
"code": "请输入验证码"
}
},
"check_qr_code": {
"title": "网上国网 App 扫码登录",
"description": "\n {qr_image} \n\n\n 请使用 网上国网 App 扫码登录,确认登录后再点击提交。"
}
},
"abort": {
"single_instance_allowed": "该集成已经配置过了,且只能配置一次。若要重新配置,请先删除旧集成。",
"get_qr_code_error": "{errmsg}",
"check_qr_code_error": "{errmsg}"
}
}
}
@@ -70,5 +70,63 @@
"get_qr_code_error": "{errmsg}", "get_qr_code_error": "{errmsg}",
"check_qr_code_error": "{errmsg}" "check_qr_code_error": "{errmsg}"
} }
},
"entity": {
"sensor": {
"balance": {
"name": "账户余额"
},
"year_ele_num": {
"name": "年度累计用电"
},
"year_ele_cost": {
"name": "年度累计电费"
},
"last_month_ele_num": {
"name": "上个月用电"
},
"last_month_ele_cost": {
"name": "上个月电费"
},
"month_ele_num": {
"name": "当月累计用电"
},
"month_p_ele_num": {
"name": "当月累计峰用电"
},
"month_v_ele_num": {
"name": "当月累计谷用电"
},
"month_n_ele_num": {
"name": "当月累计平用电"
},
"month_t_ele_num": {
"name": "当月累计尖用电"
},
"daily_ele_num": {
"name": "日总用电"
},
"daily_p_ele_num": {
"name": "日峰用电"
},
"daily_v_ele_num": {
"name": "日谷用电"
},
"daily_n_ele_num": {
"name": "日平用电"
},
"daily_t_ele_num": {
"name": "日尖用电"
},
"daily_lasted_date": {
"name": "最新日用电日期"
},
"ladder_level": {
"name": "当前阶梯"
},
"refresh_time": {
"name": "最近刷新时间"
}
}
} }
} }
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
# curl -fsSL get.hassbox.cn/state_grid | bash
set -e
export LC_ALL=en_US.UTF-8
RED_COLOR='\033[0;31m'
GREEN_COLOR='\033[0;32m'
GREEN_YELLOW='\033[1;33m'
NO_COLOR='\033[0m'
declare haPath
declare -a paths=(
"$PWD"
"$PWD/config"
"/config"
"$HOME/.homeassistant"
"/usr/share/hassio/homeassistant"
)
function info () { echo -e "${GREEN_COLOR}INFO: $1${NO_COLOR}";}
function warn () { echo -e "${GREEN_YELLOW}WARN: $1${NO_COLOR}";}
function error () { echo -e "${RED_COLOR}ERROR: $1${NO_COLOR}";}
function checkRequirement () {
if [ -z "$(command -v "$1")" ]; then
warn "'$1' 未安装,准备安装..."
apt install $1
fi
}
info "检查依赖项"
checkRequirement "wget"
checkRequirement "unzip"
for path in "${paths[@]}"; do
if [ -n "$haPath" ]; then
break
fi
if [ -f "$path/.HA_VERSION" ]; then
haPath="$path"
fi
done
if [ -z "$haPath" ]; then
echo
error "找不到 Home Assistant 根目录"
exit 1
fi
cd "$haPath"
if [ ! -d "$haPath/custom_components" ]; then
mkdir "$haPath/custom_components"
fi
cd "$haPath/custom_components"
info "检查依赖项 ok"
info "下载 state_grid 集成包"
wget "https://get.hasss.cn/state_grid.zip" >/dev/null 2>&1
info "下载 HassBox 集成包 ok"
info "state_grid 集成包解压"
if [ -d "$haPath/custom_components/state_grid" ]; then
rm -R "$haPath/custom_components/state_grid"
fi
mkdir "$haPath/custom_components/state_grid"
unzip "$haPath/custom_components/state_grid.zip" -d "$haPath/custom_components/state_grid" >/dev/null 2>&1
rm "$haPath/custom_components/state_grid.zip"
info "HassBox 集成包解压 ok"
info "安装成功!请重启 Home Assistant!如需其他帮助,可至 HassBox全屋智能 微信公众号联系客服"