Visonic / Bentel / Tyco cloud alarm — maintained fork (fixes “REST API version … not supported, Supported versions: 14.0” and the daily NoneType crash)

The Visonic cloud integration most people were using, And3rsL/VisonicAlarm-for-Hassio, was archived in December 2025 — and so was the library it depends on, And3rsL/VisonicAlarm2. Both are frozen and the open issues can’t be answered. And3rsL did all the original work and deserves the credit for it.

I’ve been maintaining a fork, and it’s now at a point where it’s worth sharing:

GitHub - Nino6689/VisonicAlarm-for-Hassio: Visonic / Bentel / Tyco alarm integration for Home Assistant — UI setup, zone bypass, panel health. Maintained fork (upstream archived Dec 2025). · GitHub

If yours stopped working, this is probably why

The most common failure is this one:

Connection failed: Rest API version 8.0, 9.0 or 10.0 is not supported by server.
Supported versions: 14.0

The old library had a hardcoded version list. This fork negotiates whatever the server advertises, so it works on REST 14.0 panels.

The other common one is an intermittent 'NoneType' object is not subscriptable — often about once a day — that a Home Assistant restart clears. That’s the cloud session expiring. The upstream code tried to reconnect, but System.is_token_valid was a @property returning the bound method API.is_logged_in without calling it, so is_token_valid == False was always False and the reconnect branch was unreachable. Sessions now re-authenticate on their own.

A third one worth knowing even if you don’t use this fork: the upstream manifest pins python-dateutil==2.7.3, a 2018 release. That pin wins inside the Home Assistant container and downgrades dateutil for every other integration you have. This fork vendors the client and has no external requirements at all.

What’s changed

  • Set up in the UI — config flow with reauth, reconfigure and options. Existing YAML is imported automatically and entity IDs are preserved, so dashboards and automations keep working.
  • Panel health — the upstream fetched troubles, alerts and alarms and then discarded them. There’s now a cloud-connection sensor (the important one: when it’s off, everything else you’re looking at is cached), active troubles named by room, per-transport connectivity, and the panel event log with the user who caused it.
  • Zone bypass — a switch per zone. Worth knowing that a bypassed zone is not reported as a trouble by the panel, so the system says “armed” while that zone does nothing. There’s a sensor for it now.
  • Every enrolled device gets an entity — keypads, sirens, smoke detectors and the PowerLink previously got none, so a keypad reporting LOW_BATTERY was invisible. That’s the complaint in upstream issues #32, #43, #48 and #57.
  • Siren actions — panic and silence, as explicit actions rather than switches.
  • The live session token is no longer published as an entity attribute. It used to reach the states API, the recorder database and any dashboard showing the panel.

Two things it does not fix

Motion detectors still don’t report live motion. The cloud only publishes whether a zone participates in the current arm mode. This is not something a different integration would solve either — on PowerMaster panels the sensors stop transmitting to the panel while it’s disarmed, to save battery, so nothing downstream can know. The Homey PowerMax app, which reads the panel over a direct serial cable, documents the same constraint.

Multiple partitions aren’t supported (upstream #41). It works against partition −1 only. If you need partitions, that’s still open.

Cloud or local?

Correction, from the author himself below: I originally described
davesmeghead/visonic as the local option. It is no
longer local-only — it has cloud capability too, currently a pre-release on HACS, tested
against a PowerLink 3.1 on REST API 14.0, and it supports multiple partitions. It also does
image/video/audio from PIR camera sensors now, and serial_proxy via ESPHome makes the wired
route much easier than it used to be. There’s a long-running thread for it
here.

So treat these as two mature options rather than a cloud one and a local one. If you want
partitions, or a wired connection, or camera media, go there. This one needs nothing but your
Visonic GO / Connect Alarm login and no hardware at all, and it’s where I’ve put the work into
panel health, per-device faults and zone bypass. We’re comparing notes.

Installing

HACS → Custom repositories → https://github.com/Nino6689/VisonicAlarm-for-Hassio, category Integration. It’s also submitted to the HACS default store, so eventually it’ll be a plain search.

Issues and PRs welcome. I’m running it against a PowerMaster 360R on REST API 14.0; reports from other panel models are especially useful, since that’s the one thing I can’t test myself.

The REST API version mismatch is a useful detail to call out, especially for installations that suddenly stopped working after the server-side version changed. The reconnect and dependency-pinning fixes also make this a practical maintenance path for older Visonic setups.

Hi, thanks for the shoutout about the local option, it’s fairly mature now. The latest addition, with the help of another developer, is that we’ve just added image/video/audio from PIR Camera sensors. Also, with ESPHome it supports serial_proxy making it even easier to setup.

But you’re incorrect about it only supporting local connections. Again, with the help of another developer we’ve added cloud capability as a “Pre-Release” on HACS. At the moment it’s only been tested with a Powerlink 3.1 hardware module and REST API 14.0. It supports multiple partitions but the two things I’m working on at the moment are the video/audio from cameras and, same as you, motion detectors reporting live action.

If you’re interested in working on it together and swapping ideas then let me know

@davesmeghead thanks — and sorry for the out-of-date framing. I’ve edited the first post: it now says yours is no longer local-only, that the cloud side is a HACS pre-release on PowerLink 3.1 / REST 14.0, that it does multiple partitions, and that serial_proxy via ESPHome has made the wired route much less daunting than it used to be. It was never meant to be “cloud vs local”, and it certainly isn’t now.

And yes — I’d like that. Here’s what I’ve got nailed down against REST 14.0 that might save you time on the cloud side, all from a PowerMaster 360R:

Auth failures don’t come back as 401. PowerManage answers 400 for a dead or unrecognised token, with the word “token” in the body. Anything watching for 401/403 to trigger reauth simply never fires, and you get a slow silent death instead. I ended up sniffing the body text on 400 to tell it apart from a genuine bad request.

The connected flag is not a connectivity signal. On broadband it flaps constantly between check-ins — I logged 444 transitions in three days, one every 10–50 minutes, each lasting about 90 seconds, and none of them a real outage. What separates a check-in gap from an actual outage is the per-transport state field (online / offline); is_connected goes false for both. Reporting the raw flag makes the entity worthless and hammers the recorder.

A bypassed zone is not reported as a trouble. The panel will happily report itself armed and ready while a zone does nothing at all. Nothing in the trouble list mentions it — you have to read traits.bypass.enabled per device.

Non-zone devices carry warnings nobody surfaces. Keypads, sirens, smoke detectors and the PowerLink have no zone, so they had no entity, so a keypad sitting on LOW_BATTERY was invisible.

On motion, I think we may genuinely have different answers rather than the same one. Over the cloud it’s not solvable: the API only publishes whether a zone participates in the current arm mode, never live state — and on PowerMaster the PIRs stop transmitting to the panel altogether while it’s disarmed, to save battery. So there’s nothing downstream to read even in principle. Sitting on the panel’s own bus you may see something the cloud never gets told about, and I’d be very interested to know either way.

On cameras I’ve got nothing useful for you — I map the cameras endpoint and read the enrolment list, but I’ve not touched the media side at all. That one’s all yours.

What I’d most want from you is partitions. Mine works against partition −1 only and it’s the biggest honest gap in it.

Best way to swap notes — GitHub issues on either repo, or DM here, whichever suits you. I’m at Nino6689/VisonicAlarm-for-Hassio; the API client is vendored in api.py with no external dependencies, so the endpoint map is all in one readable file if it’s of any use to you.

Been reading through your code to work out where my own panel actually sits, and it threw up two questions worth asking you directly rather than guessing at.

I’m on a PowerMaster 360R — which, unless I’ve misread, is one you don’t have. pyvisonic.py carries “Assume 360R is Panel 16 for this release as it was released after the PM33, also I’ve an old log file from a user that indicates this”, and AUTO_ENROL is False for both 360 and 360R on the reasoning that they’ve already got the PowerLink hardware and the panel won’t carry two connections — so it tops out at Standard Plus.

1. Does the proxy route get past that? You point 360R owners at msp1974/visonic_proxy so they can keep Ethernet and the Visonic GO app. Since the proxy sits in the path rather than enrolling as a second PowerLink, does your integration then reach full PowerLink through it on a 360R — or is it still capped at Standard Plus because of the panel type itself?

2. What does motion latency look like at full PowerLink? From the PM-360 write-up in #114, sensor triggers in Standard Plus arrive “eventually within a minute or so”. Even that is more than the cloud can ever manage — over the API there’s no live zone state at all, only whether a zone participates in the current arm mode, and on PowerMaster the PIRs stop transmitting while disarmed anyway. If full PowerLink brings that down to seconds then it’s a genuinely different capability, and I’d rather say so plainly in my post than leave people thinking the cloud limitation is universal.

And an offer, since you’re working from an assumed panel ID: I’ve got a live 360R on REST API 14.0 and I’m happy to be your test hardware for it. I can confirm or kill the Panel 16 assumption, run your cloud pre-release against it and feed back, and pull whatever logs or diagnostics would be useful. Just tell me what you want captured and I’ll get it.

Hi,
By my own admission I’m not well versed in RESP APIs, my working life has been spent with embedded systems. So it’s a steep learning curve for me.

With the Cloud connection and RESP API library:

  • Auth failures: I think I’ve got this as well, I include 400 as a trigger to reauth but feel free to make suggestions or improvements where you think can be made
  • Connected flag: I’ve noticed this as well
  • Bypassed zones: yeah, spotted that
  • Motion: I’ve concluded that as well, that we cannot solve it. However, you can set the “delay” in the panel for each PIR sensor to get it to report even when the panel is disarmed. It runs the battery down quicker but it works. I’ve got instructions on my Wiki.
  • Cameras: Via the cloud server I can trigger the creation of the video/audio but I currently have no idea how to get the actual image/audio data (see ***). I know it gets sent up to the cloud and I can see the image in the Visonic Go app.
  • Partitions: Most of the cloud messages report the partitions in use, each sensor tells you what partition(s) it is allocated to.

*** I bought a simple logic analyser that connects to USB on my PC, I can monitor the serial comms between the powerlink hardware module and the panel. If you want more of a description then let me know.

I took the Cloud REST API library from Mark Parker @msp1974 and made it asyncio to work better within home assistant. At the moment I have that code inside my integration in it’s own directory (visonic/cloud/pyvisonicalarm) so it’s easier to work on. My plan for this code is to eventually be a PyPi library so home assistant can load it, so perhaps that’s the best way we can collaborate, with a common library? Perhaps Mark would be interested as well?

When you say you’ve been reading through my code, can I just check that you’re looking at the dev release on github (and not the master release), so 0.13.0.27 is the latest.

Do you use your 360R as your main house panel or as a test panel? My main house panel is a PowerMax Pro Part. I use PowerMaster panels and a few sensors that I bought off Ebay for testing. Most of the capability is complete for PowerMax panels, but PowerMaster panel not so much. I also have an old PowerMax Pro for testing but that hardly ever comes out these days.
So I have a PowerMaster 10, 30 and a 360 (an older one without the R) for testing.

Proxy
About the proxy, this is where my knowledge is sparse and we might get Mark @msp1974 helping out here.

  1. The Panel retains it’s Powerlink hardware module
  2. The proxy creates a 3 way encoder/decoder, connecting cloud server, powerlink module and my integration (it actually creates a server that 1 or more clients can connect)
  3. The message data between the Cloud Server and the Powerlink Module is a “wrapped” version of the raw bytes normally send across the serial between the panel and the powerlink module.
  4. So by connecting my integration to the proxy as a client, I can
    • Look (read-only) at the data that the panel sends up to the cloud server
    • “Inject” commands through the proxy to the panel, such as disarm/arm etc
    • So if I inject “Arm”, as the panel sends out its updated state, the cloud accepts this as if the user has armed the panel, and I get it read-only as well
  5. As the Cloud Server sends commands to the panel, the panel reacts and changes state, which my integration reflects
  6. As I said, the data is “wrapped”, the proxy unwraps it for each client, just like it is represented on the serial link, so my code is exactly the same i.e. I process the same bytes. When I send data, the proxy wraps it up, sends it to the powerlink, which unwraps it and sends it to the panel. I hope this makes sense, perhaps a diagram is needed :smile:
  7. In addition to the visonic message data, Mark and I agreed a special protocol between us for connection/disconnection and state. Using this I tell the user that we’re in “Powerlink Bridged” Mode, but in reality it is Standard Plus internally as I do not need to do all the protocol handshaking to maintain the Powerlink connectivity.

Motion Latency
In the local code base, trigger sensors are now within a few seconds.
When a PIR sensor is triggered the panel sends an “AskMe” message. I then ask for the last time that all sensors were triggered, compare that to current time and hey presto!
As I said before, there is a way to get PIR sensors to report motion when the panel is disarmed.

Collaboration
If you’re offering to try my HACS Pre-Release with your panel then please go for it, at this moment in time the Cloud code needs as much testing as it can get and any improvements you can make to the code would be much appreciated, either for robustness, bugs or enhancements.
I’ve just finished working with another developer, he made a pull request to work on the local video/audio. We worked through the development together on that thread and then merged it in to the dev stream.
If you’re interested in making a pull request on my code (or really my updated asyncio code from @msp1974) to update the cloud side then please do, when we’re done then we can pull it out to make it a standalone library that we and anyone can use. Mark, if you’re interested as well then please raise your hand

One last question, you advertise that it’ll work with Bentel and Tyco alarms, I know they are rebranded visonic alarms but I’ve always wondered if they make differences so I’ve never been confident enough to do this?

@davesmeghead — the motion “delay” trick is gold, thank you. I’d been treating disarmed-PIR silence as a hard RF-layer wall; I didn’t know the panel could override the sensor’s own power-saving. I’ll point people at your Wiki for it. And good to have you independently confirm the 400-reauth, the connected-flag flapping and the bypass gap — reassuring they’re not just quirks of my one panel.

The reason I’m buzzing, though: I think I’ve got a concrete fix for you on the 360R.

Remember my question about whether the 360R is capped at Standard Plus? I went at the panel directly to find out — and it turns out the 360R does full PowerLink. The cap is a checksum bug, not the hardware.

Your sendPdu selects the alt CRC for 0xAB messages on PowerMaster panels:

if self.isPowerMaster() and (data[0] == 0xAB):
    sData += self._calculateCRCAlt(data)
else:
    sData += self._calculateCRC(data)

On a 360R the alt CRC is silently rejected — the panel doesn’t even ACK. And INIT/ENROL are both 0xAB messages, so on a 360R they go out with a checksum the panel discards, which looks exactly like “this panel won’t enrol.” That’s almost certainly why AUTO_ENROL ended up False for panel type 16.

Proven on hardware, same INIT one byte apart:

INIT + alt CRC → silence
INIT + std CRC → ACK, PowerLink session establishes, full B0-35 config space unlocks

So the fix looks like: use the standard CRC for 0xAB on panel type 16 (and I’d bet the 360, type 13, too).

While I was in the config space it handed over the model string as ASCII (PowerMaster360R), all the version strings, and a few B0 subtypes that aren’t in your map — 0x03 0x23 0x25 0x29 0x6D 0x6E, with 0x6E not referenced anywhere in the code.

On your logic-analyser work — that’s the part I’d most like to compare notes on. The 360R integrates the PowerLink and the panel onto one board: it’s a Tyco MSP-5, an i.MX6 SoloLite running Linux (the PowerLink side) talking to a Renesas M16C (the alarm/RF brain) over an internal UART at 38400, binary. So the serial comms you sniff between your PowerLink module and the panel, I’ve got the integrated equivalent of — same conversation, one board. Happy to swap captures if it’d help decode either end.

And yes — keen to work together. The CRC fix is yours to take. Shout if you want me to run anything against a 360R on the bench; mine’s currently in bits on my desk with a console soldered on, so I can test just about anything.

There is also linux running on the device but i cant get around the root password bootloader is liocked down

Thanks for the tip on CRC I’ll give it a try.
Just a quick one, are you sure you’re looking at and using the pre-release from HACS?
The line in sendPdu is if self.is_power_master() and (data[0] == 0xAB)
Notice is_power_master

To use the pre-release from HACS you’d need to redownload it and select “need a different version”

Again, just a quick one. I’ve changed the direct connection to what I believe creates the correct crc for the panel and pushed 0.13.0.29 as a HACS Pre-Release. Can you give it a go on your 360R panel please.

EDIT
Hi,
I got my 360 panel out to give it a try and it’s a 360R (i.e. type 16).
I’ve connected it up using serial and tried enrolling

In py_panel_type_data.py I’ve been playing with the settings but I think these should be correct for a 360R

    CFG.AUTO_ENROL     : (  None,  False,  False,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,  False ), # Early PowerMax panels cannot autoenrol to Powerlink
    CFG.AUTO_SYNCTIME  : (  None,  False,  False,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,  False ),
    CFG.POWERMASTER    : (  None,  False,  False,  False,  False,  False,  False,   True,   True,   True,   True,   True,   True,   True,   True,   True,   True,  False ), # Panels that use and respond to the additional PowerMaster Messages
    CFG.EPROM_DOWNLOAD : (  None,   True,   True,   True,   True,   True,   True,   True,  False,  False,  False,  False,  False,  False,  False,  False,  False,   True ), # Panel does EPROM Download (True) or can also do B0 Message Download (False)
    CFG.AB_CRC_TYPE_ALT: ( False,  False,  False,  False,  False,  False,  False,   True,   True,   True,   True,   True,   True,  False,   True,   True,  False,  False ), # Which CRC for 0xAB command messages. False=Normal, True=Alternate
    CFG.INIT_SUPPORT   : (  None,  False,  False,  False,   True,   True,   True,   True,   True,   True,   True,   True,   True,  False,   True,   True,   True,  False )  # Panels that support the INIT command

Every 60 seconds I try to Enrol. It sends the Enrol request to the panel, and there’s an acknowledge as it’s using the normal crc.

But the panel does not send “I’m Alive” message to denote that it is trying to maintain a powerlink connection, so the integration stays in Standard Plus, and every 60 seconds tries again to Enrol? Any Ideas?

2026-08-17 12:18:33.217 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=WaitingForEnrolSuccess     Counter=59      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=0
2026-08-17 12:18:33.217 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer]     WaitingForEnrolSuccess self._is_send_queue_empty()=True self.pmDownloadMode=False self.keep_alive_counter=9  threshold is 15
2026-08-17 12:18:34.219 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=WaitingForEnrolSuccess     Counter=60      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=0
2026-08-17 12:18:34.219 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer]     WaitingForEnrolSuccess self._is_send_queue_empty()=True self.pmDownloadMode=False self.keep_alive_counter=10  threshold is 15
2026-08-17 12:18:34.219 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] Changed state from WaitingForEnrolSuccess to EPROMExitDownload, I was in state WaitingForEnrolSuccess for approx 60 seconds
2026-08-17 12:18:34.219 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=EPROMExitDownload     Counter=0      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=0
2026-08-17 12:18:34.219 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] Changed state from EPROMExitDownload to EnrollingPowerlink, I was in state EPROMExitDownload for approx 0 seconds
2026-08-17 12:18:34.219 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=EnrollingPowerlink     Counter=0      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=0
2026-08-17 12:18:34.219 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] Try to enrol (panel PowerMaster 360R)
2026-08-17 12:18:34.219 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [sendMsgENROL] Trigger Powerlink Attempt, sending ENROL request to the panel
2026-08-17 12:18:34.219 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] Changed state from EnrollingPowerlink to WaitingForEnrolSuccess, I was in state EnrollingPowerlink for approx 0 seconds
2026-08-17 12:18:34.220 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=WaitingForEnrolSuccess     Counter=0      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=1
2026-08-17 12:18:34.220 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer]     WaitingForEnrolSuccess self._is_send_queue_empty()=False self.pmDownloadMode=False self.keep_alive_counter=11  threshold is 15
2026-08-17 12:18:34.221 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_despatcher] [sendPdu] Sent Command (Auto-Enrol PowerMax/Master)    raw data 0d ab 0a 00 00 aa aa 00 00 00 00 00 43 b1 0a   waiting for message response ['0X2']
2026-08-17 12:18:34.290 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_message_handling] [_processReceivedPacket] ACKNOWLEDGE processAB=True processB0=True processNormalData=True processDownload=False
2026-08-17 12:18:34.290 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_message_handling] [handle_msgtype02]        Received a powerlink acknowledge, I am in STANDARD_PLUS mode and sending Message RESTORE
2026-08-17 12:18:35.222 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=WaitingForEnrolSuccess     Counter=1      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=1
2026-08-17 12:18:35.222 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer]     WaitingForEnrolSuccess self._is_send_queue_empty()=False self.pmDownloadMode=False self.keep_alive_counter=1  threshold is 15
2026-08-17 12:18:36.224 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=WaitingForEnrolSuccess     Counter=2      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=1
2026-08-17 12:18:36.224 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer]     WaitingForEnrolSuccess self._is_send_queue_empty()=False self.pmDownloadMode=False self.keep_alive_counter=2  threshold is 15
2026-08-17 12:18:36.723 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_despatcher] [sendPdu] Sent Command (Restore Connection)    raw data 0d ab 06 00 00 00 00 00 00 00 00 00 43 0b 0a   waiting for message response ['0X2']
2026-08-17 12:18:36.782 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_message_handling] [_processReceivedPacket] ACKNOWLEDGE processAB=True processB0=True processNormalData=True processDownload=False
2026-08-17 12:18:37.227 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=WaitingForEnrolSuccess     Counter=3      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=0
2026-08-17 12:18:37.227 DEBUG (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer]     WaitingForEnrolSuccess self._is_send_queue_empty()=True self.pmDownloadMode=False self.keep_alive_counter=1  threshold is 15
2026-08-17 12:18:38.228 INFO (MainThread) [custom_components.visonic.direct.pyvisonic.py_visonic_sequencer] [_sequencer] SeqState=WaitingForEnrolSuccess     Counter=4      PanelMode=STANDARD_PLUS     PanelStateData=DISARMED     SendQueue=0

EDIT AGAIN:
I also send a Restore Connection powerlink command to get the panel status but it also ignores that and does not send the A5 message data that other PowerMaster panels do.

I looked up the B0 subtypes in my notes and this is what I get from my PM10 and PM30

PowerMaster 10

Received PowerMaster 10 message 03/03 (len = 3)    data = 03 03 03 07 07 76 43
            Message msgType=3 subType=3 not known about, lets see if its chunky
                       ++++++++++++++++++++++++++++++++ Message not chunky and not processed further +++++++++++++++++++++++++++++++++++++++++++++++++

Received PowerMaster 10 message 03/23 (len = 24)    data = 03 23 18 ff 08 ff 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 93 43
   msgInfo = B0_SendMessageTuple(data='ZONE_STAT23', chunky=True, paged=False)
              03 23     Decode Chunk         sequence 255  datasize 8    index MIXED   length 19     data 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Received PowerMaster 10 message 03/25 (len = 6)    data = 03 25 06 ff 08 ff 01 00 95 43
   msgInfo = B0_SendMessageTuple(data='ZONE_STAT25', chunky=True, paged=False)
              03 25     Decode Chunk         sequence 255  datasize 8    index MIXED   length 1      data 00

Received PowerMaster 10 message 03/29 (len = 18)    data = 03 29 12 ff 08 ff 0d 00 00 40 60 90 b0 b8 bc cd ce ee bc f6 98 43
   msgInfo = B0_SendMessageTuple(data='ZONE_STAT29', chunky=True, paged=False)
              03 29     Decode Chunk         sequence 255  datasize 8    index MIXED   length 13     data 00 00 40 60 90 b0 b8 bc cd ce ee bc f6

PowerMaster 30

Received PowerMaster 30 message 03/03 (len = 3)    data = 03 03 03 07 08 1d 43
            Message msgType=3 subType=3 not known about, lets see if its chunky
                       ++++++++++++++++++++++++++++++++ Message not chunky and not processed further +++++++++++++++++++++++++++++++++++++++++++++++++

Received PowerMaster 30 message 03/23 (len = 24)    data = 03 23 18 ff 08 ff 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3c 43
   msgInfo = B0_SendMessageTuple(data='ZONE_STAT23', chunky=True, paged=False)
              03 23     Decode Chunk         sequence 255  datasize 8    index MIXED   length 19     data 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Received PowerMaster 30 message 03/25 (len = 6)    data = 03 25 06 ff 08 ff 01 00 42 43
   msgInfo = B0_SendMessageTuple(data='ZONE_STAT25', chunky=True, paged=False)
              03 25     Decode Chunk         sequence 255  datasize 8    index MIXED   length 1      data 00

Received PowerMaster 30 message 03/29 (len = 18)    data = 03 29 12 ff 08 ff 0d 00 00 40 60 90 b0 b8 bc cd ce ee bc f6 48 43
   msgInfo = B0_SendMessageTuple(data='ZONE_STAT29', chunky=True, paged=False)
              03 29     Decode Chunk         sequence 255  datasize 8    index MIXED   length 13     data 00 00 40 60 90 b0 b8 bc cd ce ee bc f6

For both panels, when I ask the panel for 0x6D and 0x6E I get an Invalid Command response.
Does this help?

There are loads of B0 subtypes that I receive or can ask for but cannot decode, most of them contain zeros that give no clues. Just as an example I posted a discussion asking for help to decode a message that I can partially decode but not fully here

@davesmeghead — yes, 0.13.0.29 pre-release (not master), and the CRC fix checks out on my 360R: INIT with the standard CRC gets the ACK and opens the session, exactly as intended. Good stuff.

On your enrol wall — I reproduced it exactly on my 360R, and I think the teardown explains the “any ideas?”. I flipped the same two flags (AUTO_ENROL[16]/INIT_SUPPORT[16]) and let your engine send the real ENROL:

TX  ab 0a 00 00 <dlcode> ... 43   ENROL
RX  02 43                          panel ACK (powerlink-ack, std CRC — the fix works)
TX  ab 06 ... 43                   RESTORE
RX  02 43                          ACK
... then it loops ENROL / RESTORE / keepalive; no I'm-Alive, no A5 push; holds STANDARD_PLUS

Identical to your 360R — same ACK, same missing I’m-Alive, same 60-second futile-enrol loop. One extra data point: mine ran with the panel’s real, non-default download code (yours was aa aa / AAAA), so it isn’t the reset-panel default or your test rig — it’s the panel itself declining.

Why, I think: the 360R integrates the PowerLink onto the panel board. It’s a Tyco MSP-5 — an i.MX6 SoloLite running Linux (the PowerLink/comms side) wired to a Renesas M16C alarm/RF brain over an internal UART (38400, binary). The PowerLink role is already served on-board, so when an external serial client enrols, the panel will happily ACK the message (valid now, with your CRC fix) but won’t spin up a second live PowerLink / I’m-Alive session to it — the slot’s taken internally. That’s your “already has the hardware, won’t carry two connections” instinct, now with a physical reason under it, and it’s why the RESTORE gets ignored too.

Practical upshot, and it matches what you already tell 360R owners: on a 360R, serial tops out at Standard Plus by designAUTO_ENROL[16]=False is the right resting state (flipping it just buys the retry loop you’re seeing). The only route to PowerLink-grade push on a 360R is the proxy, sitting in the existing i.MX6 ↔ cloud PowerLink path rather than enrolling as a second one. So your proxy recommendation is the correct call, not a workaround.

Two more things from the bench:

  • Your standalone examples are broken in 0.13.0.29. simple_example.py / complete_example.py won’t import — ImportError: cannot import name 'AlTransport' from 'pyvisonic.py_abstract_classes'. It’s referenced only in those two files and defined nowhere after the direct/pyvisonic/ refactor. That’s actually why I wired a minimal client on your cvp.py pattern to drive VisonicProtocol directly. Worth a tidy so the next tester can run them.
  • The Standard-Plus path is solid on the 360R. That minimal client downloads the EPROM, IDs the panel (PowerMaster 360R), and enumerates every zone + the 2-way keypad + PGM cleanly, events flowing. So the fix gives 360R owners a fully-working Standard-Plus integration over serial — just not PowerLink.

On the B0 subtypes — thanks, that clears 0x03/0x23/0x25/0x29 up. Interesting that 0x6D/0x6E are Invalid Command on your PM10/30 but my 360R answers them, so they may be 360R-specific — I’ll poke at what they carry and send you anything decodable.

Really good progress — fix shipped and a 360R working to Standard Plus inside a day. Nice one.