Skip to content
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

Broadlink SP4M-US Integration #63853

Closed
spencerthayer opened this issue Jan 10, 2022 · 32 comments
Closed

Broadlink SP4M-US Integration #63853

spencerthayer opened this issue Jan 10, 2022 · 32 comments

Comments

@spencerthayer
Copy link

spencerthayer commented Jan 10, 2022

The problem

The problem is that the Broadlink SP4M-US use FastCon™ Bridging which is just a proprietary mesh network for Broadlink devices. According to Broadlink's documentation FastCon works as follows.

BroadLink 4th gen smart plugs support FastCon™ Bridging which allows
devices building their own sub networks with one of them acting as
master and others as clients to minimize the connection load of Wi-Fi
router. When the device was added in the network. It will look for
better network routes in 1-2 minutes and may join a sub network.

This causes issues with the existing Homeassistant integration since we cannot access the switches via their IP address. The following forum posts go into greater detail.

Is it, or could it be possible, to access and control the SP4M-US over the FastCon "sub network" via the device MAC address? It is worth noting that for all users at least one SP4M-US, functioning as the hub for FastCon, does connect to Homeassistant.

What version of Home Assistant Core has the issue?

core-2021.11.5

What was the last working version of Home Assistant Core?

core-2021.11.5

What type of installation are you running?

Home Assistant OS

Integration causing the issue

Broadlink

Link to integration documentation on our website

https://www.home-assistant.io/integrations/broadlink#switch

Additional information

The advised hack were users disconnect the each SP4M-US from the internet via the router to prevent FastCon does not work for the latest SP4M-US firmware and as of this posting there is no existing work around.

@probot-home-assistant
Copy link

broadlink documentation
broadlink source
(message by IssueLinks)

@probot-home-assistant
Copy link

Hey there @Danielhiversen, @felipediel, @L-I-Am, mind taking a look at this issue as it has been labeled with an integration (broadlink) you are listed as a code owner for? Thanks!
(message by CodeOwnersMention)

@spencerthayer
Copy link
Author

Anything y'all?

@OMVMMG
Copy link

OMVMMG commented Mar 8, 2022

Has anyone managed to get the current and power sensors working? Plugs are active using different accounts to avoid the mesh issue but neither provide data for sensors even though the sensors are recognised by Home Assistant.

@felipediel
Copy link
Contributor

Hi. We are adding support for the FastCon technology: mjg59/python-broadlink#669.

Could you help with tests? I don't have the device.

@spencerthayer
Copy link
Author

@felipediel absolutely I'll help. @OMVMMG if you can contribute too that would be awesome.

@OMVMMG
Copy link

OMVMMG commented Mar 21, 2022

@spencerthayer Happy to help. Please let me know.

@felipediel
Copy link
Contributor

Awesome, thanks! Instructions: mjg59/python-broadlink#669 (comment)

@megabitus98
Copy link

Hi. Any update here?

@spencerthayer
Copy link
Author

@megabitus98 they are working on it mjg59/python-broadlink#669 (comment)

@github-actions
Copy link

github-actions bot commented Aug 3, 2022

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.
Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍
This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale label Aug 3, 2022
@spencerthayer
Copy link
Author

spencerthayer commented Aug 3, 2022

Unless I've missed it, this issue still isn't resolved. Please keep it open.

@github-actions github-actions bot removed the stale label Aug 3, 2022
@issue-triage-workflows
Copy link

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.
Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍
This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

@felipediel
Copy link
Contributor

Let's keep it open, please. We still need to find a way to discover the subdevices. I don't have time to dedicate to this right now, any help would be appreciated.

@felipediel
Copy link
Contributor

/still valid

@TanTrungNguyen
Copy link

felipediel,

Are you using the DID (device unique identifier) with the SDK dnaControl after AddDevice?
https://docs.ibroadlink.com/public/appsdk_en/appsdk_05/#api10
See section 2.7.5

@felipediel
Copy link
Contributor

We don't use the sdk, but we have something similar. Our struggle is one step before addDevice, the question is how to discover the subdevices?

They possibly come in the end of the payload when we discover the master, I just don't have any FastCon device to confirm.

@TanTrungNguyen
Copy link

I have 3 SP4M-US that I can control from the Broadlink App.
None of them have sub device identified from the Broadlink App.

Trying the code

import broadlink as blk
device = blk.hello("192.168.0.16") # Example IP address
device.auth()
subdevices = device.get_subdevices()
did = subdevices[0].get("did")
device.get_state(did)
device.set_state(did, pwr=1)
device.set_state(did, pwr=0)
But it fails at device.get_subdevices() saying get_subdevices does not exist

Since I know the DID for each of my device from the Broadlink App
Could you use the DID directly in the get_state and set_state?

{"Device own info":{"DID":"00000000000000000000a043b0d5c527","Data Cloud":"American server","Device IP":"192.168.2.110@80","Firmware":"v57218","IoT Cloud":"American server","MAC":"a0:43:b0:d5:c5:27","PID":"0000000000000000000000008b640000","Plug-in version":"1.5.6","Routine traits":"Trigger and action","S/N code":"","SDK":"2.16.35"}}

@Kinetic69
Copy link

Kinetic69 commented May 1, 2023

We don't use the sdk, but we have something similar. Our struggle is one step before addDevice, the question is how to discover the subdevices?

They possibly come in the end of the payload when we discover the master, I just don't have any FastCon device to confirm.
@felipediel
Mate I'll send you some devices.

@felipediel
Copy link
Contributor

felipediel commented May 1, 2023

@Kinetic69 Thanks mate, I appreciate your support, but I am not sure they make these plugs to my country's specifications, we have a very specific plug unfortunately. Could you run some code with your devices instead? They need to be FastCon enabled devices configured as subdevices of a master device.

These are 3 hypotheses I have based on how other Broadlink devices work:

Test 1 - Public discovery

import socket

def discover(ip_addr):
    conn = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    conn.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    conn.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    conn.settimeout(10)
    packet = bytearray(0x30)
    packet[0x26] = 6
    checksum = sum(packet, 0xBEAF) & 0xFFFF
    packet[0x20:0x22] = checksum.to_bytes(2, "little")
    conn.sendto(packet, (ip_addr, 80))
    resp = conn.recvfrom(1024)
    conn.close()
    return resp

resp = discover("192.168.0.16")  # Example master device IP address
print(resp)

Test 2 - S2K-like discovery

import broadlink as blk
from broadlink import exceptions as e

def get_subdevices(device) -> dict:
    packet = bytearray(16)
    packet[0] = 0x06
    response = device.send_packet(0x6A, packet)
    e.check_error(response[0x22:0x24])
    payload = device.decrypt(response[0x38:])
    count = payload[0x4]
    subdevice_data = payload[0x6:]
    subdevices = [
        bytearray(subdevice_data[i * 83 : (i + 1) * 83])
        for i in range(len(subdevice_data) // 83)
    ]
    return {
        "count": count,
        "subdevices": [
            {
                "status": subdevice[0],
                "name": subdevice[4:26].decode().strip("\x00"),
                "type": subdevice[3],
                "order": subdevice[1],
                "serial": subdevice[26:30].hex(),
            }
            for subdevice in subdevices
            if any(subdevice[26:30])
        ],
    }

device = blk.hello("192.168.0.16")  # Example master device IP address
device.auth()
resp = get_subdevices(device)
print(resp)

Test 3 - S3-like discovery

import broadlink as blk
from broadlink import exceptions as e

def get_subdevices(device):
    state = {"count": 5, "index": 0}
    packet = device._encode(14, state)
    resp = device.send_packet(0x6A, packet)
    e.check_error(resp[0x22:0x24])
    return device._decode(resp)

device = blk.hello("192.168.0.16")  # Example master device IP address
device.auth()
resp = get_subdevices(device)
print(resp)

@TanTrungNguyen
Copy link

Test 1 - Public discovery response
(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb2\xd0\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8bdl\x02\xa8\xc0e\xb1\xd5\xb0C\xa025739\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x01E\x9a\x9aE\x02\x00\x00\x00\x1c\xf1[E\xdf\x0b\x00\x00\x02\x00\xa0C\xb0\xb7iL\xa0C\xb0\xd5\xc5'", ('192.168.2.108', 80))

@felipediel
Copy link
Contributor

Awesome, thanks a lot! We found something, possibily a subdevice in the end of the payload:

>>> r = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb2\xd0\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8bdl\x02\xa8\xc0e\xb1\xd5\xb0C\xa025739\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x01E\x9a\x9aE\x02\x00\x00\x00\x1c\xf1[E\xdf\x0b\x00\x00\x02\x00\xa0C\xb0\xb7iL\xa0C\xb0\xd5\xc5'"
>>> r[0x80:].hex()
'459a9a45020000001cf15b45df0b00000200a043b0b7694ca043b0d5c527'

How many subdevices that you have? Do you recognize any mac address or product id in this string?

@TanTrungNguyen
Copy link

TanTrungNguyen commented May 2, 2023

I have 3 devices with the following MAC
a0:43:b0:b7:69:4c
a0:43:b0:d5:c5:27
a0:43:b0:d5:b1:65
All 3 devices have as Device IP 192.168.2.108@80 but none has any sub device listed

@felipediel
Copy link
Contributor

felipediel commented May 2, 2023

Making some progress... the third device is the master. The MAC addresses of the first two devices start from byte 18.

>>> r = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb2\xd0\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8bdl\x02\xa8\xc0e\xb1\xd5\xb0C\xa025739\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x01E\x9a\x9aE\x02\x00\x00\x00\x1c\xf1[E\xdf\x0b\x00\x00\x02\x00\xa0C\xb0\xb7iL\xa0C\xb0\xd5\xc5'"
>>> b = r[0x80:]

>>> m1 = bytes.fromhex("a043b0b7694c")
>>> m2 = bytes.fromhex("a043b0d5c527")
>>> m3 = bytes.fromhex("a043b0d5b165")
>>> b.find(m1)
18
>>> b.find(m2)
24
>>> b.find(m3)
-1  # The third device is the master


>>> len(b)
30

# MAC address 1
>>> b[18:24].hex()
'a043b0d5c527'

# MAC address 2
>>> b[24:30].hex()
'a043b0d5c527'

# Unknown bytes
>>> b[:18].hex()
'459a9a45020000001cf15b45df0b00000200'

We still need to decode the first 18 bytes (unknown bytes). The first 4 bytes are the header:

>>> b[:4].hex()
'459a9a45'

Byte 4 to 8 is the number of devices in little-endian:

>>> int.from_bytes(b[4:8], 'little')
2

We still need to figure this out:

>>> b[8:18].hex()
'1cf15b45df0b00000200'

Do you recognize anything in this string? Do you know your product ids (devtype)? You can check them in the device info in the official app.

@TanTrungNguyen
Copy link

TanTrungNguyen commented May 2, 2023

Don't recognize byte 8:18
Here is an export of the device info and the printscreen has the product ID

{"Device own info":{"DID":"00000000000000000000a043b0d5b165","Data Cloud":"American server","Device IP":"192.168.2.114@80","Firmware":"v57218","IoT Cloud":"American server","MAC":"a0:43:b0:d5:b1:65","PID":"0000000000000000000000008b640000","Plug-in version":"1.5.6","Routine traits":"Trigger and action","S/N code":"","SDK":"2.16.35"}}

Screenshot_20230502-110244

@Kinetic69
Copy link

Sorry I don't have any WiFi enabled BL globes, only FastCon BLE enabled which I'm trying to get working.

You don't have E27 or Bayonet globe sockets in Brazil?

@ovionlogis
Copy link

@felipediel I tried running the code on my environment and got similar results

>>> r = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\xd2\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x13%:\x8ba\x0bX\xa8\xc0\x915\x00\xb0C\xa0Smart Plug 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00E\x9a\x9aE\x02\x00\x00\x00\xca\xd46RR\x0b\x00\x00\x02\x00\xa0C\xb0/\xf8\x19\xa0C\xb0\x00'\xfe"
>>> b = r[0x80:]

>>> b.hex()
'459a9a4502000000cad43652520b00000200a043b02ff819a043b00027fe'

MAC address 1

>>> b[18:24].hex()
'a043b02ff819'

MAC address 2

>>> b[24:30].hex()
'a043b00027fe'

Unknown bytes

>>> b[:18].hex()
'459a9a4502000000cad43652520b00000200'

This part is updated on every request

>>> b[8:12].hex()
'cad43652'

@ovionlogis
Copy link

@felipediel

Byte 16 to 18 is the number of devices in little-endian:

>>> int.from_bytes(b[16:18], 'little')
2

@felipediel
Copy link
Contributor

felipediel commented May 7, 2023

@ovionlogis Thanks!

b[8:12] is probably a checksum. I tried adler32 and crc32, no one matched. b[12] possibly some kind of flag and b[13] marks the end of the metadata.

I think it's possible to ignore the metadata and go straight to the mac addresses. I wish we could retrieve the device type though, I think they send another packet after this to get the type.

@Kinetic69 Yes, but they are e27 110v. I think Broadlink only makes e27 220v or e26 110v.

@Tdubs3
Copy link

Tdubs3 commented Jun 21, 2023

Any chance I could assist in implementing this same approach via BLE? I have a GW4C hub that will control devices via the cloud but I really want all of this to live locally. I’m not super savvy in decoding but have the skills and hardware to hopefully push this integration into the BLE side of things too.

@spencerthayer
Copy link
Author

I've put my Broadlink plugs in a box and forgot about them until one day I realized I threw them away. I am glad to see this issue getting some attention but at this point I think it is best for the Home Assistant enthusiast to stay away from Broad-link devices.

@issue-triage-workflows
Copy link

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.
Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍
This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

@issue-triage-workflows issue-triage-workflows bot closed this as not planned Won't fix, can't repro, duplicate, stale Oct 22, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Nov 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants