I did some more trial and error on this locally and I think there is enough here to support ACBs properly.
On my Envoy, the ACB page is under /home#acb/manage, and GET /inventory.json?deleted=1 returns the AC Batteries individually under the ACB group. In my case I have two entries there: one working battery and one faulted battery. So at least locally, they are not exposed as one single combined battery value.
For the working ACB, the inventory data includes fields like:
serial_num
sleep_enabled
sleep_min_soc
sleep_max_soc
charge_status
percentFull
producing
operating
The local control endpoint is /admin/lib/acb_config.json.
GET shows the current configured sleep request
PUT sets sleep for a specific ACB serial
DELETE clears that request again
I was able to control the working ACB through the ha_enphase_envoy_raw_data commands here: https://github.com/catsmanac/ha_enphase_envoy_raw_data.
I tested both PUT and DELETE through that raw-data integration and both return message: success.
The interesting bit is that the local “wake” action does not seem to be a separate endpoint. It looks like waking is just DELETE /admin/lib/acb_config.json for that battery serial.
From the state changes I saw, these two endpoints seem to tell different parts of the story:
/admin/lib/acb_config.json shows the requested sleep state
/inventory.json?deleted=1 shows what the battery is actually doing
For the working battery, this is what I observed:
- Normal:
sleep_enabled: false, device_status: ["envoy.global.ok"]
- Going to sleep:
sleep_enabled: true, device_status: ["envoy.global.ok"]
- Asleep:
sleep_enabled: true, device_status: ["envoy.cond_flags.pcu_ctrl.sleep-mode"]
- Waking:
sleep_enabled: false, device_status: ["envoy.cond_flags.pcu_ctrl.sleep-mode"]
So from what I can tell:
- ACBs are exposed individually in local inventory
- sleep is writable locally
- wake is the
DELETE form of the same endpoint
sleep_enabled tracks the requested sleep state
device_status is what shows normal vs asleep vs waking
That seems like enough for per-device ACB support locally, rather than only exposing one aggregate storage value.
Local Inventory Findings
Observed page:
Observed inventory endpoint:
GET /inventory.json?deleted=1
The important point for integration work is that the ACB group contains a devices array with separate per-battery objects.
Observed ACB Records
ACB 1: Working battery
part_num: 800-00930-r03
serial_num: <working_acb_serial>
device_status: envoy.global.ok in normal mode
admin_state: 2
communicating: true
provisioned: true
producing: true when awake, false when asleep
operating: true when awake, false when asleep
sleep_enabled: false when normal, true while entering sleep and while asleep, false while waking
percentFull: 57 while entering sleep, 48 while asleep and waking in the captured examples
maxCellTemp: 24 while entering sleep, 25 while asleep and waking in the captured examples
sleep_min_soc: normal value observed as 25, commanded values observed as 65 and 45
sleep_max_soc: normal value observed as 30, commanded values observed as 70 and 50
charge_status: discharging while awake and while entering sleep, idle while asleep and waking
img_pnum_running: 520-00092-r01-v02.13.02
ptpn: 540-00155-r01-v02.13.01
ACB 2: Non-working or faulted AC Battery
part_num: 800-00930-r03
serial_num: <faulted_acb_serial>
device_status: envoy.cond_flags.acb_ctrl.cellminvoltagewarning, envoy.cond_flags.acb_ctrl.bmusenseerror
admin_state: 3
img_pnum_running: 520-00092-r01-v02.13.02
ptpn: 540-00155-r01-v02.13.01
Fields such as sleep_enabled, percentFull, charge_status, producing, and operating were present on the healthy battery record and absent from the faulted battery record in the provided payload.
Confirmed Local Control Endpoint Behavior
Observed control endpoint:
GET /admin/lib/acb_config.json
PUT /admin/lib/acb_config.json
DELETE /admin/lib/acb_config.json
Normal mode read
Observed HA action:
action: enphase_envoy_raw_data.read_data
data:
config_entry_id: 01KNPGAK95DTK3950B33FG5JE1
endpoint: /admin/lib/acb_config.json
Observed response:
/admin/lib/acb_config.json:
acb_sleep: []
This indicates that no active sleep request is configured in normal mode.
Command battery to go to sleep
Observed HA action:
action: enphase_envoy_raw_data.send_data
data:
config_entry_id: 01KNPGAK95DTK3950B33FG5JE1
endpoint: /admin/lib/acb_config.json
method: PUT
risk_acknowledged: true
test_mode: false
data:
acb_sleep:
- serial_num: "<working_acb_serial>"
sleep_min_soc: 65
sleep_max_soc: 70
Observed response:
/admin/lib/acb_config.json:
message: success
Observed immediate follow-up read:
/admin/lib/acb_config.json:
acb_sleep:
- serial_num: "<working_acb_serial>"
sleep_min_soc: 65
sleep_max_soc: 70
This confirms:
- the endpoint is writable through the HA raw-data integration
- the gateway accepts per-battery sleep requests by serial number
- the GET endpoint reflects the configured per-battery sleep request immediately after the PUT
- the working ACB can be put into sleep through the
ha_enphase_envoy_raw_data commands
Cancel pending sleep or wake battery
Observed HA action:
action: enphase_envoy_raw_data.send_data
data:
config_entry_id: 01KNPGAK95DTK3950B33FG5JE1
endpoint: /admin/lib/acb_config.json
method: DELETE
risk_acknowledged: true
test_mode: false
data:
acb_sleep:
- serial_num: "<working_acb_serial>"
Observed response:
/admin/lib/acb_config.json:
message: success
Observed read behavior around wake:
{"acb_sleep":[{"serial_num":"<working_acb_serial>"}]}
followed by:
{"acb_sleep":[]}
This confirms:
DELETE is accepted by the local endpoint
- the same
DELETE path is used both to cancel a pending sleep request and to wake a sleeping battery
- the local gateway does not require a separate wake endpoint for ACBs
- the working ACB can be woken through the
ha_enphase_envoy_raw_data commands
Confirmed State Transitions
The most useful result from the trial-and-error data is that the battery state can be inferred by combining /admin/lib/acb_config.json with /inventory.json?deleted=1.
1. Normal mode
Observed GET /admin/lib/acb_config.json:
/admin/lib/acb_config.json:
acb_sleep: []
Observed GET /inventory.json?deleted=1 for the healthy battery:
{
"serial_num": "<working_acb_serial>",
"device_status": ["envoy.global.ok"],
"admin_state": 2,
"producing": true,
"communicating": true,
"provisioned": true,
"operating": true,
"sleep_enabled": false,
"percentFull": 57,
"maxCellTemp": 24,
"sleep_min_soc": 25,
"sleep_max_soc": 30,
"charge_status": "discharging"
}
Normal-mode markers:
sleep_enabled: false
device_status: ["envoy.global.ok"]
producing: true
operating: true
charge_status: "discharging"
2. Entering sleep mode
Observed GET /admin/lib/acb_config.json while entering sleep:
{"acb_sleep": [{"serial_num": "<working_acb_serial>","sleep_min_soc": 65,"sleep_max_soc": 70}]}
Observed GET /inventory.json?deleted=1 while entering sleep:
{
"serial_num": "<working_acb_serial>",
"device_status": ["envoy.global.ok"],
"admin_state": 2,
"producing": true,
"communicating": true,
"provisioned": true,
"operating": true,
"sleep_enabled": true,
"percentFull": 57,
"maxCellTemp": 24,
"sleep_min_soc": 65,
"sleep_max_soc": 70,
"charge_status": "discharging"
}
Entering-sleep markers:
sleep_enabled: true
device_status still remains envoy.global.ok
producing and operating can still remain true
charge_status can still remain discharging
The only immediately visible state change in the captured entering-sleep example was sleep_enabled switching from false to true.
3. Fully asleep
Observed GET /admin/lib/acb_config.json while asleep:
{"acb_sleep": [{"serial_num": "<working_acb_serial>","sleep_min_soc": 45,"sleep_max_soc": 50}]}
Observed GET /inventory.json?deleted=1 while asleep:
{
"serial_num": "<working_acb_serial>",
"device_status": ["envoy.cond_flags.pcu_ctrl.sleep-mode"],
"admin_state": 2,
"producing": false,
"communicating": true,
"provisioned": true,
"operating": false,
"sleep_enabled": true,
"percentFull": 48,
"maxCellTemp": 25,
"sleep_min_soc": 45,
"sleep_max_soc": 50,
"charge_status": "idle"
}
Asleep markers:
sleep_enabled: true
device_status: ["envoy.cond_flags.pcu_ctrl.sleep-mode"]
producing: false
operating: false
charge_status: "idle"
4. Waking or editing sleep mode
Observed GET /inventory.json?deleted=1 after DELETE /admin/lib/acb_config.json while the battery was still transitioning:
{
"serial_num": "<working_acb_serial>",
"device_status": ["envoy.cond_flags.pcu_ctrl.sleep-mode"],
"admin_state": 2,
"producing": false,
"communicating": true,
"provisioned": true,
"operating": false,
"sleep_enabled": false,
"percentFull": 48,
"maxCellTemp": 25,
"sleep_min_soc": 25,
"sleep_max_soc": 30,
"charge_status": "idle"
}
Waking markers:
sleep_enabled: false
device_status can still remain envoy.cond_flags.pcu_ctrl.sleep-mode
producing and operating can still remain false
charge_status can still remain idle
This shows that sleep_enabled clears before device_status returns to normal. That is the clearest evidence in the current dataset that waking is a distinct transition state rather than an instantaneous return to envoy.global.ok.
How To Tell What The Battery Is Doing
Based on the captured data, the local state can be inferred like this:
Awake
sleep_enabled: false
device_status: ["envoy.global.ok"]
Going to sleep
sleep_enabled: true
device_status: ["envoy.global.ok"]
Asleep
sleep_enabled: true
device_status: ["envoy.cond_flags.pcu_ctrl.sleep-mode"]
Waking
sleep_enabled: false
device_status: ["envoy.cond_flags.pcu_ctrl.sleep-mode"]
Interpretation
- The local gateway inventory response can identify ACB units individually by serial number.
- The local response exposes per-battery health and operational fields, not only aggregate storage totals.
- A healthy ACB record includes battery-control-related fields such as
sleep_enabled, sleep_min_soc, sleep_max_soc, charge_status, producing, and operating.
- The faulted ACB still appears in inventory as a distinct AC Battery, which is useful for discovery and diagnostics.
PUT /admin/lib/acb_config.json returns success and persists per-battery sleep requests.
GET /admin/lib/acb_config.json reflects configured sleep requests, not the full live operating state by itself.
DELETE /admin/lib/acb_config.json returns success and acts as the local wake or cancel path.
/inventory.json?deleted=1 is the better source for the actual exposed battery state.
sleep_enabled appears to represent the requested sleep state.
device_status appears to represent the operating state transition more directly.
- The combined state machine is now visible enough to support richer local integration logic.
Remaining Unknowns
The main remaining unknowns are narrower now:
- how long it takes after
DELETE for device_status to return from envoy.cond_flags.pcu_ctrl.sleep-mode to envoy.global.ok
- whether there are any additional intermediate
device_status values during wake on other firmware versions
- whether invalid serial numbers or invalid SoC ranges return useful validation errors
- whether the gateway ever removes sleeping entries from
/admin/lib/acb_config.json automatically without a DELETE
Relevant ACB Payload Excerpts
Normal ACB excerpt
{
"type": "ACB",
"devices": [
{
"part_num": "800-00930-r03",
"installed": "1769471139",
"serial_num": "<working_acb_serial>",
"device_status": [
"envoy.global.ok"
],
"last_rpt_date": "1775654518",
"admin_state": 2,
"dev_type": 11,
"created_date": "1769471139",
"img_load_date": "1625324297",
"img_pnum_running": "520-00092-r01-v02.13.02",
"ptpn": "540-00155-r01-v02.13.01",
"chaneid": 1795162641,
"device_control": [
{
"gficlearset": false
}
],
"producing": true,
"communicating": true,
"provisioned": true,
"operating": true,
"sleep_enabled": false,
"percentFull": 57,
"maxCellTemp": 24,
"sleep_min_soc": 25,
"sleep_max_soc": 30,
"charge_status": "discharging"
},
{
"part_num": "800-00930-r03",
"installed": "1769471087",
"serial_num": "<faulted_acb_serial>",
"device_status": [
"envoy.cond_flags.acb_ctrl.cellminvoltagewarning",
"envoy.cond_flags.acb_ctrl.bmusenseerror"
],
"last_rpt_date": "1770429494",
"admin_state": 3,
"dev_type": 11,
"created_date": "1769471087",
"img_load_date": "1662811078",
"img_pnum_running": "520-00092-r01-v02.13.02",
"ptpn": "540-00155-r01-v02.13.01",
"chaneid": 1795162385,
"device_control": [
{
"gficlearset": false
}
]
}
]
}
Sleeping ACB excerpt
{
"type": "ACB",
"devices": [
{
"part_num": "800-00930-r03",
"installed": "1769471139",
"serial_num": "<working_acb_serial>",
"device_status": [
"envoy.cond_flags.pcu_ctrl.sleep-mode"
],
"last_rpt_date": "1775659175",
"admin_state": 2,
"dev_type": 11,
"created_date": "1769471139",
"img_load_date": "1625324297",
"img_pnum_running": "520-00092-r01-v02.13.02",
"ptpn": "540-00155-r01-v02.13.01",
"chaneid": 1795162641,
"device_control": [
{
"gficlearset": false
}
],
"producing": false,
"communicating": true,
"provisioned": true,
"operating": false,
"sleep_enabled": true,
"percentFull": 48,
"maxCellTemp": 25,
"sleep_min_soc": 45,
"sleep_max_soc": 50,
"charge_status": "idle"
},
{
"part_num": "800-00930-r03",
"installed": "1769471087",
"serial_num": "<faulted_acb_serial>",
"device_status": [
"envoy.cond_flags.acb_ctrl.cellminvoltagewarning",
"envoy.cond_flags.acb_ctrl.bmusenseerror"
],
"last_rpt_date": "1775658742",
"admin_state": 3,
"dev_type": 11,
"created_date": "1769471087",
"img_load_date": "1662811078",
"img_pnum_running": "520-00092-r01-v02.13.02",
"ptpn": "540-00155-r01-v02.13.01",
"chaneid": 1795162385,
"device_control": [
{
"gficlearset": false
}
]
}
]
}
Notes:
- Local polling can already see ACBs individually
- A healthy ACB exposes battery-specific sleep and charge state fields
- Faulted ACBs still appear as distinct AC Batteries
- The local control endpoint is confirmed writable for both sleep and wake or cancel actions
DELETE /admin/lib/acb_config.json is the local wake path
- The battery state machine can be inferred from
sleep_enabled plus device_status
- The remaining gap is not discovery or control availability, but only the finer timing semantics during wake transitions