Automation to change the Payload for https://iess3.hanchuess.com/gateway/platform/pcs/historyStaticsChart on the first day of each month.
NOTE: the REST in CONFIG.YAML for historyStaticsChart remains as it is now. This process will overwrite it with 2 python (.PY) files and an AUTOMATION.
This is a 4 step process:-
STEP 1
1… Create a file in File Editor named hanchu_encrypt.py
2… Copy the contents of hanchu_encrypt.txt to this file.
#!/usr/bin/env python3
"""
Hanchu iESS Payload Encryptor
Generates AES-128-CBC encrypted payloads.
Key and IV: [YOUR SECRET KEY HERE]
Requires: pycryptodome
pip install pycryptodome
"""
import base64
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
KEY = b"YOUR SECRET KEY HERE"
IV = b"YOUR SECRET KEY HERE"
SN = "YOUR INVERTER SERIAL NUMBER HERE"
DEVTYPE = "2"
def encrypt_raw(payload: dict) -> str:
"""Encrypt any dict payload as-is."""
plaintext = json.dumps(payload, separators=(',', ':'))
cipher = AES.new(KEY, AES.MODE_CBC, IV)
ct = cipher.encrypt(pad(plaintext.encode('utf-8'), AES.block_size))
return base64.b64encode(ct).decode('utf-8')
def encrypt(value: dict) -> str:
"""Encrypt an iotSet value dict wrapped in the standard structure."""
return encrypt_raw({"devType": DEVTYPE, "value": value, "sn": SN})
def decrypt(b64: str) -> str:
"""Decrypt a base64 AES-CBC string back to plaintext."""
ct = base64.b64decode(b64)
cipher = AES.new(KEY, AES.MODE_CBC, IV)
return unpad(cipher.decrypt(ct), AES.block_size).decode('utf-8')
def to_seconds(t: str) -> int:
"""Convert HH:MM time string to seconds from midnight."""
h, m = map(int, t.split(":"))
return h * 3600 + m * 60
# ── Other endpoint payload builders ───────────────────────────────────────────
def history_statics_chart(year_month: str) -> str:
"""
Generate payload for historyStaticsChart endpoint.
year_month: 'YYYY-MM' e.g. '2026-03'
"""
return encrypt_raw({
"sn": SN,
"devType": DEVTYPE,
"maxCount": 1440,
"dateStr": year_month,
"masterSum": True
})
# ── Main ───────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print()
print("=" * 70)
print("HISTORY STATICS CHART PAYLOADS")
print("=" * 70)
import datetime
now = datetime.datetime.now()
this_month = now.strftime("%Y-%m")
last_month = (now.replace(day=1) - datetime.timedelta(days=1)).strftime("%Y-%m")
print(f" This month ({this_month}): {history_statics_chart(this_month)}")
print(f" Last month ({last_month}): {history_statics_chart(last_month)}")
As you will see you will need to add the “Secret Key” & the serial number which is unique to your system.
The serial number is easy as you will find it on both the home pages of the app & console. It will show “SN: YOUR SERIAL NUMBER”.
The “SECRET KEY” will be found in dev tools when using the console.
Go to SOURCES
Tap/open “file-cdn.hanchuess.com”
Tap/open “_/iess3/assets”
Tap/open “index-05281ed2.js”
In this file, search for “AES”. There should be 6 instances of this. It is when you come to the 6th instance you should see something like this:
function fge(e, t) {
typeof e != “string” && (e = JSON.stringify(e));
const n = po.AES.encrypt(e, po.enc.Utf8.parse(t), {
iv: po.enc.Utf8.parse (“YOUR SECRET KEY”),
mode: po.mode.CBC
});
Once both these are added save the file.
You can test output works by testing in terminal.
To test first load using:
pip install pycryptodome
Then test in terminal by typing:
python3 /config/hanchu_encrypt.py
If it produces the required output (it will print the current months & last months payload). Check the current month’s Payload with the Payload you have currently in the REST. They should match.
STEP 2
1… Create a file in File Editor named update_history_payload.py
2… Copy the contents of update_history_payload.txt to this file.
#!/usr/bin/env python3
"""
Run on the 1st of each month to update the historyStaticsChart payload
in configuration.yaml with the current month's encrypted payload.
"""
import sys
import datetime
import re
sys.path.insert(0, '/config')
from hanchu_encrypt import history_statics_chart
# Generate new payload for current month
new_payload = history_statics_chart(datetime.datetime.now().strftime('%Y-%m'))
# Read config
with open('/config/configuration.yaml', 'r') as f:
lines = f.readlines()
# Find the historyStaticsChart resource line, then find the payload line after it
updated = False
in_history_block = False
for i, line in enumerate(lines):
if 'historyStaticsChart' in line:
in_history_block = True
if in_history_block and line.strip().startswith('payload:'):
lines[i] = line[:line.index('payload:')] + 'payload: "' + new_payload + '"\n'
updated = True
break
if not updated:
print("ERROR: Pattern not found - payload not updated")
sys.exit(1)
new_config = ''.join(lines)
# Write back
with open('/config/configuration.yaml', 'w') as f:
f.write(new_config)
print(f"SUCCESS: Updated payload for {datetime.datetime.now().strftime('%Y-%m')}")
print(f"New payload: {new_payload}")
Nothing needs changing in this file.
STEP 3
You need to add to your CONFIG.YAML the following:
shell_command:
update_hanchu_history_payload: "python3 /config/update_history_payload.py”
DON’T FORGET THE INDENTATION.
STEP 4
AUTOMATION
I have named “Update Hanchu History Payload Monthly”
This will run at 5 minutes pass midnight on the 1st of each month and will update the historyStaticsChart Payload & restart CONFIG.YAML.
alias: Update Hanchu History Payload Monthly
description: >
On the 1st of each month, regenerates the encrypted historyStaticsChart
payload for the new month and reloads REST sensors.
triggers:
- at: "00:05:00"
trigger: time
conditions:
- condition: template
value_template: "{{ now().day == 1 }}"
actions:
- action: shell_command.update_hanchu_history_payload
- delay: "00:00:10"
- action: rest.reload
mode: single
There you go. Good luck.