-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to get/set schedule. #18
Comments
From your diagnostics the issue is not related to the integration, but to the PyPlumIO library, which handles low-level protocol. From a few cues, such as incorrect fuel level, missing model string and partially working schedules, it seems that Plum changed structure of related frames for your model. As PyPlumIO was initially writen for ecoMAX 850P in 2022, changes in firmware done by Plum in further models/firmwares can break the library's ability to decode frames. This prompts future reverse-engineering of what changed in your device firmware. I'll get to it on the weekends, hovewer it will most likely take more than couple days. Sorry for inconvenience! |
I confirm that my controller is ecoMAX 860P6-O. If I can help you with anything, write please. Regards Jarek |
Just so that I have somewhere to start, could you please elaborate if there any errors, when trying to set water heater schedule using |
Hi
There are no errors after calling the service. There are no errors in the
HA logs.
Regards, Jarek
śr., 13 gru 2023 o 02:37 Denis Paavilainen ***@***.***>
napisał(a):
… Just so that I have somewhere to start, could you please elaborate if
there any errors, when trying to set water heater schedule using
set_schedule service or it just silently fails?
—
Reply to this email directly, view it on GitHub
<#18 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AR7UDQNAKMUBL2DV5N34TVDYJEBFJAVCNFSM6AAAAABAK37CP2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNJTGEZDQMRYG4>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
This is not good :( If you still want to help and know how to run arbitrary python scripts, could you please run one below and send me If you don't know how to run python scripts or not comfortable with doing so, don't worry, I'll still try to solve this issue, it's just gonna take more time. If you decided to help, this simple heavily-commented script will simply send a request for schedules to your boiler and once it'll get a response, it'll save raw bytes and decoded data into JSON file. This way I can obtain raw bytes of the schedule response from your device and try to deduce why it is decoded incorrectly. Don't forget to install PyPlumIO via pip before running this script:
and set HOST and PORT variables to point to your converter. """Collect a single frame and dumps it to the file."""
import asyncio
import json
import sys
import pyplumio
from pyplumio.const import DeviceType
from pyplumio.frames import requests, responses
from pyplumio.helpers.parameter import ParameterValues
# Set host and port here.
HOST = "localhost"
PORT = 8899
# ------------------------------------- #
# Do not edit anything below this line. #
# ------------------------------------- #
FILENAME = "schedules.json"
def encode_parameter_values(o):
"""Encode ParameterValues to JSON."""
if isinstance(o, ParameterValues):
o = {
"value": o.value,
"min_value": o.min_value,
"max_value": o.max_value,
}
return o
async def main() -> int:
"""Collect a single schedule response frame and dump it to the JSON file.
We'll use DummyProtocol here, since we'll need direct control over
connection and access to a raw ecoMAX frame.
"""
async with pyplumio.open_tcp_connection(
host=HOST, port=PORT, protocol=pyplumio.DummyProtocol()
) as connection:
print("Requesting schedules from the ecoMAX...")
await connection.writer.write(
requests.SchedulesRequest(recipient=DeviceType.ECOMAX)
)
print("Waiting for response...")
while connection.connected:
if isinstance(
(response := await connection.reader.read()),
responses.SchedulesResponse,
):
print(f"Got response ({response.length} bytes): {response.hex()}")
break
print(f"Saving frame to {FILENAME}...")
with open(FILENAME, "w", encoding="UTF-8") as file:
# We'll open a file and save dictionary containing
# our frame's message represented as hex string and
# decoded frame data.
file.write(
json.dumps(
{
"message": response.hex(),
"data": response.data,
},
default=encode_parameter_values,
indent=2,
)
)
print("All done!")
sys.exit(asyncio.run(main())) |
Hi
It sends you the collected data. Are they helpful to you?
Regards, Jarek
czw., 14 gru 2023 o 03:19 Denis Paavilainen ***@***.***>
napisał(a):
… This is not good :(
If you still want to help and know how to run arbitrary python scripts,
could you please run one below and send me schedules.json file that it'll
produce.
If you don't know how to run python scripts or not comfortable with doing
so, don't worry, I'll still try to solve this issue, it's just gonna take
more time.
If you decided to help, this simple heavily-commented script will simply
send a request for schedules to your boiler and once it'll get a response,
it'll save raw bytes and decoded data into JSON file. This way I can obtain
raw bytes of the schedule response from your device and try to deduce why
it is decoded incorrectly.
Don't forget to install PyPlumIO via pip before running this script:
$ pip install pyplumio
and set HOST and PORT variables to point to your converter.
"""Collect a single frame and dumps it to the file."""
import asyncioimport jsonimport sys
import pyplumiofrom pyplumio.const import DeviceTypefrom pyplumio.frames import requests, responsesfrom pyplumio.helpers.parameter import ParameterValues
# Set host and port here.HOST = "localhost"PORT = 8899
# ------------------------------------- ## Do not edit anything below this line. ## ------------------------------------- #FILENAME = "schedules.json"
def encode_parameter_values(o):
"""Encode ParameterValues to JSON."""
if isinstance(o, ParameterValues):
o = {
"value": o.value,
"min_value": o.min_value,
"max_value": o.max_value,
}
return o
async def main() -> int:
"""Collect a single schedule response frame and dump it to the JSON file. We'll use DummyProtocol here, since we'll need direct control over connection and access to a raw ecoMAX frame. """
async with pyplumio.open_tcp_connection(
host=HOST, port=PORT, protocol=pyplumio.DummyProtocol()
) as connection:
print("Requesting schedules from the ecoMAX...")
await connection.writer.write(
requests.SchedulesRequest(recipient=DeviceType.ECOMAX)
)
while connection.connected:
print("Waiting for response...")
if isinstance(
(response := await connection.reader.read()),
responses.SchedulesResponse,
):
print(f"Got response ({response.length} bytes): {response.hex()}")
break
print(f"Saving frame to {FILENAME}...")
with open(FILENAME, "w", encoding="UTF-8") as file:
# We'll open a file and save dictionary containing
# our frame's message represented as hex string and
# decoded frame data.
file.write(
json.dumps(
{
"message": response.hex(),
"data": response.data,
},
default=encode_parameter_values,
indent=2,
)
)
print("All done!")
sys.exit(asyncio.run(main()))
—
Reply to this email directly, view it on GitHub
<#18 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AR7UDQJPKSBZQB5EXE3DGVLYJJO3BAVCNFSM6AAAAABAK37CP2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNJVGAYDIMBTGI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
It doesn't send it to me, it simply writes it to the file named |
Thank you very much for your help! I'll plug this data into a test case and try to rewrite decoder until it matches this for heating schedule:
|
Hi, Today I had more time to look at the schedules. I have to apologize to you because I misled you earlier.
I once wrote to you that I can read the DHW schedule, but I cannot set it. It turned out that I could set it up, but I was doing it incorrectly. This is because, before I installed the integration, I had set a schedule on my room panel (ecoMAX 860P Touch) for: 00.00-06.00 night (means do not heat the water), 06.00-15.00 day (heat water), 15.00-24.00 night. Then I started the service and set, for example, 12.00-14.00 day. There was no change on the room panel so I thought the service was not working properly. Now I know that to set water heating between 12.00-14.00 I should call the service 06.00-11.30 night and the second service 14.00-15.00 night. Alternatively 00.00:23.30 night and then 12.00-13.30 day. I don't know how to do it in Lovelace yet - maybe you can tell me something ;)
As I wrote earlier here, I could neither read nor save the schedule. Today I noticed that Plum ecoMAX 860P6-O + Plum ecoMAX 860P6-O TOUCH has two schedules for CH. One is in the pellet boiler and the other in the room panel. They are independent and their settings do not overlap. I checked that I use the service to set the schedule in the pellet boiler, but I don't use it (it's turned off), I use the one in the room panel. Tell me, are you able to improve the integration so that it allows you to change the schedule in the room panel? If something is not clear to you, do not hesitate to ask. Regards, Jarek |
Hi! Thank you for the clarification!
"""Collect a ecoSTER schedules frame and dumps it to the file."""
import asyncio
import json
import sys
from typing import ClassVar
import pyplumio
from pyplumio import ChecksumError
from pyplumio.const import DeviceType
from pyplumio.frames import Request, Response, bcc
from pyplumio.helpers.parameter import ParameterValues
from pyplumio.stream import FrameReader, FrameWriter, struct_header
# Set host and port here.
HOST = "localhost"
PORT = 8899
# ------------------------------------- #
# Do not edit anything below this line. #
# ------------------------------------- #
FILENAME = "schedules.json"
class CustomFrameType:
"""Extend frame type enum with ecoSTER schedule frames."""
REQUEST_ECOSTER_SCHEDULES = 88
RESPONSE_ECOSTER_SCHEDULES = 216
class CustomRequest(Request):
"""Custom request frame."""
__slots__ = ("frame_type",)
frame_type: ClassVar[CustomFrameType | int]
class CustomResponse(Response):
"""Custom response frame."""
__slots__ = ("frame_type",)
frame_type: ClassVar[CustomFrameType | int]
class CustomFrameReader(FrameReader):
"""Custom frame reader that allows unknown frames."""
async def read(self) -> CustomResponse | None:
"""Read any frames from the stream.."""
(
header,
length,
recipient,
sender,
sender_type,
econet_version,
) = await self._read_header()
if recipient not in (DeviceType.ECONET, DeviceType.ALL):
return None
payload = await self._reader.readexactly(length - struct_header.size)
if payload[-2] != bcc(header + payload[:-2]):
raise ChecksumError(f"Incorrect frame checksum ({payload[-2]})")
response = CustomResponse(
recipient=recipient,
message=payload[1:-2],
sender=sender,
sender_type=sender_type,
econet_version=econet_version,
)
response.frame_type = payload[0]
return response
class CustomDummyProtocol(pyplumio.DummyProtocol):
"""A dummy protocol with a custom reader that accepts unknown frames."""
def connection_established(
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
) -> None:
"""Set reader and writer attributes and set the connected event."""
self.reader = CustomFrameReader(reader)
self.writer = FrameWriter(writer)
self.connected.set()
def encode_parameter_values(o):
"""Encode ParameterValues to JSON."""
if isinstance(o, ParameterValues):
o = {
"value": o.value,
"min_value": o.min_value,
"max_value": o.max_value,
}
return o
async def main() -> int:
"""Collect a single schedule response frame and dump it to the JSON file.
We'll use DummyProtocol here, since we'll need direct control over
connection and access to a raw ecoMAX frame.
"""
async with pyplumio.open_tcp_connection(
host=HOST, port=PORT, protocol=CustomDummyProtocol()
) as connection:
print("Requesting ecoSTER schedules from the ecoMAX...")
request = CustomRequest(recipient=DeviceType.ECOMAX)
request.frame_type = CustomFrameType.REQUEST_ECOSTER_SCHEDULES
await connection.writer.write(request)
print("Waiting for response...")
while connection.connected:
response: CustomResponse = await connection.reader.read()
if (
response is not None
and response.frame_type == CustomFrameType.RESPONSE_ECOSTER_SCHEDULES
):
print(f"Got response ({response.length} bytes): {response.hex()}")
break
print(f"Saving frame to {FILENAME}...")
with open(FILENAME, "w", encoding="UTF-8") as file:
# We'll open a file and save dictionary containing
# our frame's message represented as hex string and
# decoded frame data.
file.write(
json.dumps(
{
"message": response.hex(),
"data": response.data,
},
default=encode_parameter_values,
indent=2,
)
)
print("All done!")
sys.exit(asyncio.run(main())) |
Hi There is some problem with this script. When I run it, in the terminal I have: Requesting ecoSTER schedules form tne ecoMAX... The script doesn't end. On the RS485 to ethernet converter page, I see that the script is trying to send data, but ecoMAX returns only 10 bytes of data. I checked, the previous script executes correctly, so it's not a hardware fault. Regards, Jarek |
It's means that ecoMAX didn't recognize a request and didn't respond to it. Let me check if there are any additional request body requirements... edit. Try now. This time we'll request schedules from the panel. If it scripts hang on waiting for response you can cancel it by pressing """Collect a single frame and dumps it to the file."""
import asyncio
import json
import sys
from typing import ClassVar
import pyplumio
from pyplumio import ChecksumError
from pyplumio.const import DeviceType
from pyplumio.frames import Request, Response, bcc
from pyplumio.helpers.parameter import ParameterValues
from pyplumio.stream import FrameReader, FrameWriter, struct_header
# Set host and port here.
HOST = "localhost"
PORT = 8899
# ------------------------------------- #
# Do not edit anything below this line. #
# ------------------------------------- #
FILENAME = "schedules.json"
class CustomFrameType:
"""Extend frame type enum with ecoSTER schedule frames."""
REQUEST_ECOSTER_SCHEDULES = 88
RESPONSE_ECOSTER_SCHEDULES = 216
class CustomRequest(Request):
"""Custom request frame."""
__slots__ = ("frame_type",)
frame_type: ClassVar[CustomFrameType | int]
class CustomResponse(Response):
"""Custom response frame."""
__slots__ = ("frame_type",)
frame_type: ClassVar[CustomFrameType | int]
class CustomFrameReader(FrameReader):
"""Custom frame reader that allows unknown frames."""
async def read(self) -> CustomResponse | None:
"""Read any frames from the stream.."""
(
header,
length,
recipient,
sender,
sender_type,
econet_version,
) = await self._read_header()
if recipient not in (DeviceType.ECONET, DeviceType.ALL):
return None
payload = await self._reader.readexactly(length - struct_header.size)
if payload[-2] != bcc(header + payload[:-2]):
raise ChecksumError(f"Incorrect frame checksum ({payload[-2]})")
response = CustomResponse(
recipient=recipient,
message=payload[1:-2],
sender=sender,
sender_type=sender_type,
econet_version=econet_version,
)
response.frame_type = payload[0]
return response
class CustomDummyProtocol(pyplumio.DummyProtocol):
"""A dummy protocol with a custom reader that accepts unknown frames."""
def connection_established(
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
) -> None:
"""Set reader and writer attributes and set the connected event."""
self.reader = CustomFrameReader(reader)
self.writer = FrameWriter(writer)
self.connected.set()
def encode_parameter_values(o):
"""Encode ParameterValues to JSON."""
if isinstance(o, ParameterValues):
o = {
"value": o.value,
"min_value": o.min_value,
"max_value": o.max_value,
}
return o
async def main() -> int:
"""Collect a single schedule response frame and dump it to the JSON file.
We'll use DummyProtocol here, since we'll need direct control over
connection and access to a raw ecoMAX frame.
"""
async with pyplumio.open_tcp_connection(
host=HOST, port=PORT, protocol=CustomDummyProtocol()
) as connection:
print("Requesting schedules from the ecoSTER...")
request = CustomRequest(recipient=DeviceType.ECOSTER)
request.frame_type = CustomFrameType.REQUEST_ECOSTER_SCHEDULES
await connection.writer.write(request)
print("Waiting for response...")
while connection.connected:
response: CustomResponse = await connection.reader.read()
if (
response is not None
and response.frame_type == CustomFrameType.RESPONSE_ECOSTER_SCHEDULES
):
print(f"Got response ({response.length} bytes): {response.hex()}")
break
print(f"Saving frame to {FILENAME}...")
with open(FILENAME, "w", encoding="UTF-8") as file:
# We'll open a file and save dictionary containing
# our frame's message represented as hex string and
# decoded frame data.
file.write(
json.dumps(
{
"message": response.hex(),
"data": response.data,
},
default=encode_parameter_values,
indent=2,
)
)
print("All done!")
sys.exit(asyncio.run(main())) |
Now it's ok. I see that the script collected very little data, but maybe this is what you need. Regards, Jarek |
Yes, thank you very much! The collected data seemed short to you, because unlike previous script, it lacks decoded part, since PyPlumIO doesn't know how to decode this kind of message yet.
I'll need to think about how to best integrate this with HASS, since we now have separate schedule requests for two different devices, but I'll get it done until integration version 0.4.2. Thanks again for your help! It's greatly appreciated! |
Ok. I will wait for the development of integration. If you need additional data, do not hesitate to write. |
Hello, I see that you have released version 0.42 of integration in which you have separated ecoSTER as a separate device. I updated the integration but I still don't see the option to change schedules on ecoSTER. Am I doing something wrong or have you not added this functionality yet? Regards, Jarek |
Hi, ecoSTER schedules are not part of current minor release. Development timeline got a bit mangled, due to me having very little time to work on the project during almost all of January and early February. Many small fixes and changes are accumulated during the time, so it made more sense to release them as separate minor version. I try not include many bugfixes and large features in the same release together, since large new features tend to cause issues for some people, and then they are forced to choose between downgrading and having old unfixed bugs or upgrading and getting new ones, introduced by the new features. I'll revisit ecoSTER schedules once custom entities PR are done and close this issue, when schedules are implemented. Sorry for the delay. |
Is there an existing issue for this?
I'm having the following issue:
When I run the "Plum ecoMAX: get_schedule" service for heating, the integration returns all values as "day". This is not correct because in the controller I have it set from 00.00 to 06.00 "night" from 6.00 to 15.00 "day" from 15.00 to 00.00 "night" "
I can't save the schedule to the controller using the Plum ecoMAX service: set_schedule" for heating.
When I run the "Plum ecoMAX: get_schedule" service for water heater, the integration returns the correct values.
But I can't save the schedule to the controller using the Plum ecoMAX service: set_schedule" for the water heater.
I have following devices connected:
I'm connecting to my devices using:
Ethernet/WiFi to RS-485 converter
I'm seeing following log messages:
No response
My diagnostics data:
config_entry-plum_ecomax-3d173fc70d439aca63ce86c959ccc479.json.txt
Code of Conduct
The text was updated successfully, but these errors were encountered: