Skip to content

44 ocpp integration support#67

Merged
dirkgroenen merged 10 commits into
dirkgroenen:44-ocpp-integration-supportfrom
juliusvaart:44-ocpp-integration-support
Oct 18, 2025
Merged

44 ocpp integration support#67
dirkgroenen merged 10 commits into
dirkgroenen:44-ocpp-integration-supportfrom
juliusvaart:44-ocpp-integration-support

Conversation

@juliusvaart
Copy link
Copy Markdown

@juliusvaart juliusvaart commented Sep 21, 2025

I've changed the way ha_device finds the entity names and made fixes to the ocpp charger.

@dirkgroenen dirkgroenen linked an issue Sep 22, 2025 that may be closed by this pull request
@kljakobsen
Copy link
Copy Markdown

just a quick update.
I did a 1.0.5 installation. Merged the #44 and the #45. Added the changes by Julius Vaart.
Everything installed fine and no errors in the logs. However there is no adjusting the current
1
2
So I think we are close, but not yet there.
evse_load_balancer_44.tar.gz
attached is 2 screenshots and the EVSE after merging

@juliusvaart
Copy link
Copy Markdown
Author

Hi @kljakobsen,

I've rewritten the set_current_limit function to set the number of maximum_current instead of using the ocpp service set max current. Could you test this latest commit?

Copy link
Copy Markdown
Owner

@dirkgroenen dirkgroenen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for helping along @juliusvaart! Unfortunately AI seems to have taken a little spin on the code which I feel made it enter the wrong rabbit hole.

I left some replies I would like to ask you to check to see whether we can make it work. For the set_current_limit implementation I left a suggest implementation in my comment, following the protocol implementation standards.

For the second one related to finding the entitiy by its key I think AI went on a little spin and made some complex changes, while the actual solution was just to change OcppEntityMap to:

class OcppEntityMap:
    """
    Map OCPP entities to their respective attributes.

    Based on the enums and measurands from the OCPP integration:
    https://github.com/lbbrhzn/ocpp/blob/main/custom_components/ocpp/enums.py
    """

    # Status entities from HAChargerStatuses
    Status = "status"
    StatusConnector = "status_connector"

    # Current limit via number metric
    # https://github.com/lbbrhzn/ocpp/blob/main/custom_components/ocpp/number.py
    MaximumCurrent = "maximum_current"

    TransactionId = "transaction_id"

May I ask you to make these changes and test if it works? I can also push the changes myself to your branch if you prefer, such that you can test them, that's not a problem for me.

min_current = min(limit.values())

# Build the entity_id for the "Maximum current" number entity
entity_id = f"number.{self._get_ocpp_devid()}_maximum_current"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking at the docs reviewing this PR when I stumbled upon this entry:

When the OCPP integration is added to your Home Assistant, you get a slider to control the maximum charge current named (or one per connector, if your charger has multiple connectors): number.<name_ocpp_charger>_maximum_current

While using this entity in your automation might seem logical, it could potentially lead to permanent damage to your charger in the long run. This entity controls the OCPP ChargePointMaxProfile, which configures the maximum power or current available for the entire charging station. This setting is typically written to non-volatile storage (like EEPROM or flash memory) to persist across reboots. Frequent writes to these types of memory can accelerate wear, potentially shortening the lifespan of your charger. Ten updates per day is no problem at all, 1 update per 10s could break your charger somewhere between 3 days and 3 years depending on the HW solution.

https://github.com/lbbrhzn/ocpp/blob/3ce65ab8351c1228f941c9cfa413d4de891ee1c3/docs/Charge_automation.md#adjusting-the-charge-current

I therefore don't recommend going this approach, as we're basically changing the chargers "global max current" setting (which is what get_max_current_limit() should return) and risk breaking chargers.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest the following, leveraging ChargePointMaxProfile instead of TxProfile to set the entire Charge Points max (ampcontrol.io wrote a nice doc about OCPP)

async def set_current_limit(self, limit: dict[Phase, int]) -> None:
    """
    Set the current limit for the charger.

    OCPP chargers typically support setting current limits through
    the set_charge_rate service or smart charging profiles.
    As OCPP may not support per-phase limits, we'll use the minimum value.
    """
    min_current = min(limit.values())

    transaction_id = self._get_entity_id_by_key(OcppEntityMap.TransactionId)

    try:
        await self.hass.services.async_call(
            domain=CHARGER_DOMAIN_OCPP,
            service="set_charge_rate",
            service_data={
                "custom_profile": {
                    "transactionId": transaction_id,
                    "chargingProfileId": 1,
                    "stackLevel": 0,
                    "chargingProfilePurpose": "ChargePointMaxProfile",
                    "chargingProfileKind": "Relative",
                    "chargingSchedule": {
                        "chargingRateUnit": "A",
                        "chargingSchedulePeriod": [
                            {"startPeriod": 0, "limit": min_current}
                        ]
                    }
                },
                "conn_id": 1
            },
            blocking=True,
        )
    except (ValueError, RuntimeError, TimeoutError) as e:
        _LOGGER.warning(
            "Failed to set current limit for OCPP charger %s: %s",
            self.device_entry.id,
            e,
        )

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try this! Totally missed this doc from the OCPP integration...

Comment thread custom_components/evse_load_balancer/chargers/ocpp_charger.py
Comment on lines +86 to +91
def _get_ocpp_devid(self) -> str:
"""Extract OCPP charge point identity (devid) from device identifiers."""
for id_domain, ident in self.device_entry.identifiers:
if id_domain == CHARGER_DOMAIN_OCPP:
return ident
return getattr(self.device_entry, "name", None) or self.device_entry.id
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this was used in combination with get_ocpp_device to compose an entity_id, but there's already a _get_entity_id_by_key() method which searches through the entities related to the device and grab the one ending with the provided key (maximum_current) for example.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok! This can go. I'm not the best when it comes to RTFM...

def _get_entity_id_by_key(self, entity_key: str) -> float | None:
"""
Get the entity ID for a given key.
def _get_entity_id_by_key(self, entity_key: str) -> str:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you rewrote this method, but are not using it in your (latest) changes in the actual OcppCharger class. The AI code also feels a bit like a shot in the dark, and makes me wonder if we're not just declaring the wrong OcppEntityMap for StatusConnector and Status).

Since there's a bunch of char replacement (_ for . eg) I feel like we can probably just fix this by changing the entity map. Unfortunately I don't have an OCPP setup myself, so I can't see how the device exposes the entities.

Can you check in your Developer Tools if entities aren't just exposed as sensor.{device_id}_status_connector for example?

If that's the case we don't need these changes in here, but simply just a change in the OcppEntittyMap.

Copy link
Copy Markdown
Author

@juliusvaart juliusvaart Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default with one socket is: sensor.charger_status_connector and sensor.charger_status so yes: sensor.{device_id}_status_connector ('charger' is the default).

From the OCPP manual:

Your charger exposes a connector status sensor:

Single-connector: sensor.<charger_id>_status_connector

Multi-connector: sensor.<charger_id>_connector_<connector_number>_status_connector

See: https://home-assistant-ocpp.readthedocs.io/en/latest/user-guide.html#understanding-status

@kljakobsen
Copy link
Copy Markdown

I may add, the the current version of ocpp_charger.py in Julius' repository doesn't work as intended. It simply does not change the max current settings correctly/accordingly to available current L1-L3.
In the screen dumps below, it is clear that the maximum current (charger_maximum_current) isnt adjusted from 15A to 14A. I have it at this for at least 25 min. No adjustments were made.
The EVSE logbook clearly says it should be adjusted´, however the OCPP logbook also shows no adjustments.
3
4

@juliusvaart
Copy link
Copy Markdown
Author

juliusvaart commented Oct 6, 2025

I made the changes as suggested but now "EVSE Load Balancer Load balancing state" is not "Monitoring loads" anymore but stays on "Awaiting charger".

@dirkgroenen
Copy link
Copy Markdown
Owner

I made the changes as suggested but now "EVSE Load Balancer Load balancing state" is not "Monitoring loads" anymore but stays on "Awaiting charger".

Your car is plugged in and ready to charge? If so what's the state and entitiy_id of status and status_connector entities?

@juliusvaart
Copy link
Copy Markdown
Author

sensor.charger_status_connector is Charging

sensor.charger_status is Available

@dirkgroenen
Copy link
Copy Markdown
Owner

sensor.charger_status_connector is Charging

sensor.charger_status is Available

That's interesting. I wrote a specific test just now to test your scenario, and it should set can_charge() to True, which is the method used to understand whether you can actually charge.

image

Can you go to Developer Tools -> Template and past the following template:

"{{ device_entities("xxx") }}"

Replace xxx for your Device ID (you can find that in the URL, going to the OCCP Charger's page and grab the ID from the URL.

I'm assuming you'll end up with the same entities, but just to verify if they are connected to the device.

The load balancer should:

  • Grab all entities related to the device (charger) you provided
  • Use _get_entity_id_by_key to grab the entity which unique_id ends with the provided key (status or connectr_status in our case).

If it can't find the entity it should raise an exception, or log something, so also have a look at your debug logs and paste them here if you wish.

@juliusvaart
Copy link
Copy Markdown
Author

That's interesting. I wrote a specific test just now to test your scenario, and it should set can_charge() to True, which is the method used to understand whether you can actually charge.

I cant find where you committed this test?

@juliusvaart
Copy link
Copy Markdown
Author

Because with the latest code in my fork i get this when calling the device_entities() in dev-tools:

[
  "sensor.evse_load_balancer_load_balancing_state",
  "sensor.evse_load_balancer_available_current_l1",
  "sensor.evse_load_balancer_available_current_l2",
  "sensor.evse_load_balancer_available_current_l3"
]

@juliusvaart
Copy link
Copy Markdown
Author

@dirkgroenen I don't really know how to continue testing/improving this PR.

@dirkgroenen dirkgroenen force-pushed the 44-ocpp-integration-support branch from eb8440f to ca27bb5 Compare October 18, 2025 10:42
@dirkgroenen dirkgroenen merged commit b30b3e9 into dirkgroenen:44-ocpp-integration-support Oct 18, 2025
1 check passed
@dirkgroenen
Copy link
Copy Markdown
Owner

dirkgroenen commented Oct 18, 2025

@juliusvaart I've merged your branch in its latest state in the other one (#47) we have going on. Just to make things a bit easier and consolidate the different work streams.

I'll go through that PR now, make sure specs are working, read through our history again and see where we are with things at the moment. I'll drop you a comment in #47 in a bit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CHARGER] OCPP integration support

3 participants