krauskopf / node-red-contrib-car-bmw

Node-RED nodes for BMW ConnectedDrive
Other
11 stars 8 forks source link

Add charging profile #25

Open amuehlhause opened 1 year ago

amuehlhause commented 1 year ago

I've added "get charging profile" and "change charging mode" service.

For "get charging profile" I extended the getCarInfo node and reused the GET_CHARGING_PROFILE service. I've added the VIN to the request headers as this is required for that new service.

For "change charging mode" I extended the executeRemoteService node and added SERVICE_CHANGE_CHARGING_MODE. I've added the possibility to submit a payload body beside query parameters as this is required for that new service. For sake of simplicity the body can be submitted via msg.payload to the executeRemoteService node.

The payload body of "change charging mode" is equivalent to a subset (chargeAndClimateTimerDetail) of the response of "get charging profile" service. Additionally you can provide "servicePack" attribute directly to the payload and "timerChange=NO_CHANGE" to the "chargingMode" object of the payload (not documented yet).

It's recommended to always call "get charging profile" service and then use the response to create the payload for "change charging mode" service with the above mentioned optional additions. For sake of simplicity and flexibility I've not added this logic, yet.

patrickvandenbulcke commented 1 year ago

Hello, I tested the Add_Charging_Profile branch.. However I want to change the max AMP for AC load: chargingAndClimateProfile.chargeAndClimateSettings.chargingSettings.acCurrentLimitLabel="16A"; When I change this payload received from ChargingProfile and provide it as an input for ChangeChargingMode I get an http bad request as response.

Is this because I can only provide a subset of the "chargeAndClimateTimerDetail" that is supported? because I would actually need to set "chargingSettingsDetail" in order to change the chargingSettingsDetail.acLimit.current.value parameter

Sorry, if this is not the place to put this, but I can't find anywhere where I should ask this question :)

krauskopf commented 1 year ago

Hi, sorry for the delay. I'm still on holiday and will be back home till end of next week. I will have a look on your issue and this PR as soon as I'm back.

krauskopf commented 1 year ago

Hi @amuehlhause,

thanks for your contribution. I finally managed to integrate your code into v0.5.1, but somehow got not everything working with my 330e. For example, I could change the charging timer, but not set the charging mode to TIME_SLOT or CHARGING_WINDOW. Are there any other preconditions? I have attached my example flows. Maybe you can have a look at it. Thanks! grafik

[{"id":"f3c8ff33346c864f","type":"car-bmw-action","z":"77482163.aa1be","account":"20974e09.9f9e82","name":"","action":"CHANGE_CHARGING_MODE","x":790,"y":2060,"wires":[["7d5c1640a51ca4dc"]]},{"id":"7636eb380adcbaa4","type":"inject","z":"77482163.aa1be","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":2060,"wires":[["8e63bd09e18f76b8"]]},{"id":"8e63bd09e18f76b8","type":"car-bmw-get","z":"77482163.aa1be","account":"20974e09.9f9e82","name":"","datatype":"chargingprofile","x":300,"y":2060,"wires":[["04d7e419aa1839fc"]]},{"id":"04d7e419aa1839fc","type":"function","z":"77482163.aa1be","name":"Change to timeslot","func":"\nif (!msg.payload.hasOwnProperty('chargeAndClimateTimerDetail')) {\n    node.error(\"Received invalid data, can not proceed.\", msg);\n    return;\n}\n\n// Prepare the payload by copying the current values\nmsg.payload = {\n    \"chargingMode\": msg.payload.chargeAndClimateTimerDetail.chargingMode,\n    \"isPreconditionForDepartureActive\": msg.payload.chargeAndClimateTimerDetail.isPreconditionForDepartureActive,\n    \"departureTimer\": msg.payload.chargeAndClimateTimerDetail.departureTimer,\n    \"servicePack\": msg.payload.servicePack\n};\n\n// Use this code for setting a time slot\nmsg.payload.chargingMode.type = 'TIME_SLOT';\nmsg.payload.chargingMode.chargingPreference = 'CHARGING_WINDOW';\nmsg.payload.chargingMode.timerChange = \"NO_CHANGE\";\n\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":2060,"wires":[["df0b35683f7fa7b3","f3c8ff33346c864f"]]},{"id":"df0b35683f7fa7b3","type":"debug","z":"77482163.aa1be","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":720,"y":2120,"wires":[]},{"id":"7d5c1640a51ca4dc","type":"debug","z":"77482163.aa1be","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1040,"y":2060,"wires":[]},{"id":"bfd2977aa5b2e4c4","type":"comment","z":"77482163.aa1be","name":"Example: Change chargingMode to TIME_SLOT","info":"","x":200,"y":2000,"wires":[]},{"id":"0ce0cf3e1813c1e8","type":"comment","z":"77482163.aa1be","name":"Example: Change chargingMode to IMMEDIATELY","info":"","x":210,"y":2180,"wires":[]},{"id":"a8429fbd9e2e4e47","type":"car-bmw-action","z":"77482163.aa1be","account":"20974e09.9f9e82","name":"","action":"CHANGE_CHARGING_MODE","x":790,"y":2240,"wires":[["6f19096fd894872c"]]},{"id":"e57492623458d48d","type":"inject","z":"77482163.aa1be","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":2240,"wires":[["f82d1daebd256afc"]]},{"id":"f82d1daebd256afc","type":"car-bmw-get","z":"77482163.aa1be","account":"20974e09.9f9e82","name":"","datatype":"chargingprofile","x":300,"y":2240,"wires":[["1bcde8b35ca7c002"]]},{"id":"1bcde8b35ca7c002","type":"function","z":"77482163.aa1be","name":"Change to immediate","func":"\nif (!msg.payload.hasOwnProperty('chargeAndClimateTimerDetail')) {\n    node.error(\"Received invalid data, can not proceed.\", msg);\n    return;\n}\n\n// Prepare the payload by copying the current values\nmsg.payload = {\n    \"chargingMode\": msg.payload.chargeAndClimateTimerDetail.chargingMode,\n    \"isPreconditionForDepartureActive\": msg.payload.chargeAndClimateTimerDetail.isPreconditionForDepartureActive,\n    \"departureTimer\": msg.payload.chargeAndClimateTimerDetail.departureTimer,\n    \"servicePack\": msg.payload.servicePack\n};\n\n// Use this code for charging immediately\nmsg.payload.chargingMode.type = 'CHARGING_IMMEDIATELY';\nmsg.payload.chargingMode.chargingPreference = 'NO_PRESELECTION';\nmsg.payload.chargingMode.timerChange = \"NO_CHANGE\";\n\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":2240,"wires":[["b5a8b6423e7ca815","a8429fbd9e2e4e47"]]},{"id":"b5a8b6423e7ca815","type":"debug","z":"77482163.aa1be","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":720,"y":2300,"wires":[]},{"id":"6f19096fd894872c","type":"debug","z":"77482163.aa1be","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1040,"y":2240,"wires":[]},{"id":"19f90399cdbbe6b9","type":"comment","z":"77482163.aa1be","name":"Example: Change the time slot for loading","info":"","x":180,"y":2340,"wires":[]},{"id":"a713d33f74e9ea67","type":"car-bmw-action","z":"77482163.aa1be","account":"20974e09.9f9e82","name":"","action":"CHANGE_CHARGING_MODE","x":790,"y":2400,"wires":[["ae2b2797476191f3"]]},{"id":"25c6d02ea1de4264","type":"inject","z":"77482163.aa1be","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":2400,"wires":[["b0047b35a3f5da99"]]},{"id":"b0047b35a3f5da99","type":"car-bmw-get","z":"77482163.aa1be","account":"20974e09.9f9e82","name":"","datatype":"chargingprofile","x":300,"y":2400,"wires":[["33300adf95b0a486"]]},{"id":"33300adf95b0a486","type":"function","z":"77482163.aa1be","name":"Change to timeslot","func":"\nif (!msg.payload.hasOwnProperty('chargeAndClimateTimerDetail')) {\n    node.error(\"Received invalid data, can not proceed.\", msg);\n    return;\n}\n\n// Prepare the payload by copying the current values\nmsg.payload = {\n    \"chargingMode\": msg.payload.chargeAndClimateTimerDetail.chargingMode,\n    \"isPreconditionForDepartureActive\": msg.payload.chargeAndClimateTimerDetail.isPreconditionForDepartureActive,\n    \"departureTimer\": msg.payload.chargeAndClimateTimerDetail.departureTimer,\n    \"servicePack\": msg.payload.servicePack\n};\n\n// Use this code for setting a time slot\nmsg.payload.chargingMode.type = 'TIME_SLOT';\nmsg.payload.chargingMode.chargingPreference = 'CHARGING_WINDOW';\nmsg.payload.chargingMode.startTimeSlot = \"0001-01-01T02:00:00.000\";\nmsg.payload.chargingMode.endTimeSlot = \"0001-01-01T03:00:00.000\";\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":2400,"wires":[["a17ab1d5afa674e5","a713d33f74e9ea67"]]},{"id":"a17ab1d5afa674e5","type":"debug","z":"77482163.aa1be","name":"debug 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":720,"y":2460,"wires":[]},{"id":"ae2b2797476191f3","type":"debug","z":"77482163.aa1be","name":"debug 8","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1040,"y":2400,"wires":[]},{"id":"f836278c59e8bd77","type":"function","z":"77482163.aa1be","name":"Set ac limit and target value","func":"\nmsg.payload = {\n    \"chargingTarget\": 80, \n    \"acLimitValue\": 6\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":2580,"wires":[["98cdd5e0ecf56e2b"]]},{"id":"dc3ed992da4ec921","type":"inject","z":"77482163.aa1be","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":2580,"wires":[["f836278c59e8bd77"]]},{"id":"98cdd5e0ecf56e2b","type":"car-bmw-action","z":"77482163.aa1be","account":"20974e09.9f9e82","name":"","action":"CHANGE_CHARGING_SETTINGS","x":660,"y":2580,"wires":[["6a3422370f80fdd9"]]},{"id":"6a3422370f80fdd9","type":"debug","z":"77482163.aa1be","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":950,"y":2580,"wires":[]},{"id":"31bc49d40ae0a984","type":"comment","z":"77482163.aa1be","name":"Example: Change AC Limit and battery target value","info":"","x":210,"y":2520,"wires":[]},{"id":"20974e09.9f9e82","type":"car-bmw","name":"","region":"0","unit":"metric","debug":true}]
amuehlhause commented 4 months ago

Sorry for late reply @krauskopf . I always focused on climate and not charging. Can you please provide your payload?

That worked for me (with focus on climate): {"chargingMode":{"type":"CHARGING_IMMEDIATELY","startTimeSlot":"0001-01-01T02:20:00","endTimeSlot":"0001-01-01T00:00:00","chargingPreference":"NO_PRESELECTION"},"isPreconditionForDepartureActive":true,"departureTimer":{"type":"WEEKLY_DEPARTURE_TIMER","weeklyTimers":[{"id":1,"time":"0001-01-01T08:30:00","daysOfTheWeek":[],"timerAction":"DEACTIVATE"},{"id":2,"time":"0001-01-01T00:00:00","daysOfTheWeek":[],"timerAction":"DEACTIVATE"},{"id":3,"time":"0001-01-01T15:18:00","daysOfTheWeek":[],"timerAction":"DEACTIVATE"},{"id":4,"time":"0001-01-01T07:45:00","daysOfTheWeek":[],"timerAction":"ACTIVATE"}]},"servicePack":"ATM02"}

Maybe "servicePack":"ATM02" is important (sorry cannot remember). For the payload try BMW Get node for the data type Charging Profile before - so you see the structure (everything in chargeAndClimateTimerDetail object).