andersevenrud / ha-nexa-bridge-x

Home Assistant - Nexa Bridge X Integration
Other
9 stars 3 forks source link

Support for legacy Nexa Bridge (aka Bridge1) #15

Closed biodland closed 1 year ago

biodland commented 1 year ago

Trying to add this to my Nexa bridge, but seems to not be working. Getting invalid authentication.

Have probed the device with Postman and in Chrome and the API's are exposed. Been looking briefly into code and seems like the issue lies in the basicauth part, as per documentation of Nexa API it states the requirement of digest auth.

https://nexa.se/docs/

general For the local connection, the auth is a simple HTTP Digest Auth. Username and password is nexa. Always use the prefix /v1 before any API-call. For example, http://192.168.1.12/v1/nodes.

And doing a simple test in postman shows that using basicauth will not work, and changing to digestauth works. If this is a difference between Nexa Bridge X and the older Nexa Bridge I'm not aware. Could not find a simple way to use digestauth in the same modules. And due to lack of python experience this gets overwhelming fast. :)

image image image

andersevenrud commented 1 year ago

Thanks for trying it out! You might actually be the first :)

If this is a difference between Nexa Bridge X and the older Nexa Bridge I'm not aware

This might be the case, because this integration works great on my bridge. I'm going to look into this today.

Would you mind sharing some data from the /v1/info endpoint ? It might have some personal stuff in there so make sure to remove that before pasting.l

biodland commented 1 year ago
{
    "systemType": "Bridge1",
    "builddate": "2020-02-04T12:50:18CET",
    "gitversion": "8a57926552033f755c83e3e9e97419edf3eaf6a7",
    "version": "1.6.7",
    "gwid": "4b0783",
    "sunrise": "08:26",
    "sunset": "17:20",
    "location": {
        "ip": "1.2.3.4",
        "lat": 1.0,
        "lon": 1.0,
        "manualOverride": true
    },
    "currentTime": "2023-02-11T12:57:33+0100",
    "name": "Nexa Bridge",
    "externalIp": "1.2.3.4",
    "remoteSupportPort": 0,
    "internalIp": "10.0.0.226",
    "zwaveRole": "Primary",
    "zwaveOnOtherNetwork": false,
    "ok433": true,
    "zwaveOK": true,
    "cloudOK": true,
    "cloudLatency": 36,
    "upgraded": false
}
andersevenrud commented 1 year ago

Ah. This integration was made for the "Bridge 2" aka "Bridge X".

I don't think the "Bridge 1" responds with the same data from the API, which seems to be the case by briefly looking at one of your screenshots.

Might be possible to add support though, but then we need to dig some more in order to make things compatible (and I don't have one of these).

andersevenrud commented 1 year ago

which seems to be the case by briefly looking at one of your screenshots.

Maybe you could share all the data from the nodes endpoint ? Also does it have /v1/energy ?

biodland commented 1 year ago

v1_energy.txt v1_info.txt v1_nodes.txt v1_rooms.txt v1_scenarios.txt

biodland commented 1 year ago

Added the API end points, seems like most of the api endpoints listed on the nexa official site is available. What I could not find was an automations endpoint, not sure how implemented that is yet.

Since all the information on that page is swedish notes. :)

andersevenrud commented 1 year ago

Thanks!

I looked through and it seems that the node data is mostly compatible, however it does not contain the current value(s).

Maybe it's possible to get that from /v1/nodes/<node id>.

If so then I think I can make reading sensor data work for the legacy bridge in this integration 🀞

biodland commented 1 year ago

This is the result for Node Id it /1

{
    "includedSecurely": false,
    "id": 1,
    "type": "ZWave",
    "hideInApp": false,
    "ignoreInRoomAction": false,
    "broken": false,
    "locked": false,
    "name": "",
    "roomId": 1,
    "groupNode": 0,
    "prio": 0,
    "favorite": false,
    "capabilities": [],
    "zwaveInfo": {
        "basic": 2,
        "generic": 2,
        "specific": 1
    },
    "lastEvents": {},
    "failed": false
}

For id 1017

{
    "id": 1017,
    "type": "NexaSwitch",
    "hideInApp": false,
    "ignoreInRoomAction": false,
    "broken": false,
    "locked": false,
    "name": "Utelys terasse",
    "roomId": 1,
    "groupNode": 0,
    "prio": 0,
    "favorite": false,
    "capabilities": [
        "switchBinary"
    ],
    "lastEvents": {
        "switchBinary": {
            "value": false,
            "sourceNode": 1017,
            "fromWS": false,
            "time": "2023-02-11T08:28:00+0100",
            "name": "switchBinary"
        },
        "methodCall": {
            "cap": "switchBinary",
            "value": false,
            "targetNode": 1017,
            "targetRoomId": 0,
            "method": "setValue",
            "sourceNode": 1017,
            "fromWS": false,
            "time": "2023-02-03T20:49:42+0100",
            "name": "methodCall"
        }
    }
}
andersevenrud commented 1 year ago

Sweet! That's basically all that's needed to make this work.

I'm going to set up a dummy API to simulate a classic bridge and then update the integration.

If you want to I can drop some development builds into this issue for you to test when I have something worked out.

biodland commented 1 year ago

Sounds good. :)

andersevenrud commented 1 year ago

I think I have something ready for testing now.

Added a "legacy bridge" checkbox in the configuration box that changes the behaviour of this integration slightly to work with your device.

nexa_bridge_x.zip

andersevenrud commented 1 year ago

I wonder if your device also broadcasts updates via a websocket. Currently I have it disabled because I was unsure.

Would you be able to run https://github.com/vi/websocata and connect to the socket at ws://<bridge-ip>:8887 to see if you get any connection and data when one of your connected devices updates (like pressing a switch).

If this is the same as on Bridge2 I can simply just re-enable.

biodland commented 1 year ago

Did a test with an websocket test app on the google play store. And got some good news. :) if it is implemented same as Bridge2 i dont know.

Screenshot_20230211_192133

andersevenrud commented 1 year ago

Fett! This looks promising.

Could you paste in the full text here ? I need to see the entire message(s).

biodland commented 1 year ago

power:{"scale":2,"meterType":"Electric","value":0.0,"electricScale":"W","sourceNode":2,"fromWS":false,"time":"2023-02-11T19:21:24+0100","name":"power"}

This was the complete message when i tutned off my light.

andersevenrud commented 1 year ago

Hm. This doesn't look like a message from the actual switch, but some kind of power metering.

The bridge can spit out quite a few messages in a row (my assumption is that metering gets triggered when a switch state is modified and the "power" comes right after the value we want), and I don't know if your app is capable of capturing that πŸ€”

biodland commented 1 year ago

Might be correct. Will check on the computer soon.

andersevenrud commented 1 year ago

In hindsight, I think that the message you gave was enough to work with. So I've created a new release that's ready for testing:

nexa_bridge_x.zip

andersevenrud commented 1 year ago

was enough to work with

Would still love to see some though 😊

biodland commented 1 year ago

image

andersevenrud commented 1 year ago

Looks like my changes should take care of the differences in your data :)

biodland commented 1 year ago
2023-02-11 22:52:26.421 WARNING (MainThread) [homeassistant.config_entries] Config entry 'Nexa Bridge' for nexa_bridge_x integration not ready yet: 'prevValue'; Retrying in background
2023-02-11 22:52:33.748 ERROR (MainThread) [custom_components.nexa_bridge_x.nexa] Unexpected error fetching Nexa Bridge X Coordinator data: 'prevValue'
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 239, in _async_refresh
    self.data = await self._async_update_data()
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 624, in _async_update_data
    list(map(lambda n: NexaNode(n, self.legacy), nodes)),
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 624, in <lambda>
    list(map(lambda n: NexaNode(n, self.legacy), nodes)),
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 456, in __init__
    legacy and data["value"] or data["prevValue"],
KeyError: 'prevValue'
biodland commented 1 year ago

Trying to add the comment as code, but it does remove linebreaks. Dont understand what I'm doing wrong x)

andersevenrud commented 1 year ago

That was a silly mistake on my end... Fixed here hopefully:

nexa_bridge_x.zip

Trying to add the comment as code, but it does remove linebreaks. Dont understand what I'm doing wrong x)

You need three of those tickmarks and a newline, then end with a newline and three ticks :) I think you can click on edit on your last comment (that I edited) to see.

biodland commented 1 year ago
root@cloud:/var/homeassistant# cat home-assistant.log
2023-02-11 23:10:39.409 WARNING (SyncWorker_2) [homeassistant.loader] We found a custom integration nexa_bridge_x which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you experience issues with Home Assistant
2023-02-11 23:10:39.410 WARNING (SyncWorker_2) [homeassistant.loader] We found a custom integration hacs which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you experience issues with Home Assistant
2023-02-11 23:11:02.980 ERROR (MainThread) [custom_components.nexa_bridge_x.nexa] Unexpected error fetching Nexa Bridge X Coordinator data: 'value'
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 239, in _async_refresh
    self.data = await self._async_update_data()
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 622, in _async_update_data
    list(map(lambda n: NexaNode(n, self.legacy), nodes)),
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 622, in <lambda>
    list(map(lambda n: NexaNode(n, self.legacy), nodes)),
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 453, in __init__
    data["value"],
KeyError: 'value'
2023-02-11 23:11:02.981 WARNING (MainThread) [homeassistant.config_entries] Config entry 'Nexa Bridge' for nexa_bridge_x integration not ready yet: 'value'; Retrying in background
2023-02-11 23:11:09.380 ERROR (MainThread) [custom_components.nexa_bridge_x.nexa] Unexpected error fetching Nexa Bridge X Coordinator data: 'value'
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 239, in _async_refresh
    self.data = await self._async_update_data()
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 622, in _async_update_data
    list(map(lambda n: NexaNode(n, self.legacy), nodes)),
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 622, in <lambda>
    list(map(lambda n: NexaNode(n, self.legacy), nodes)),
  File "/config/custom_components/nexa_bridge_x/nexa.py", line 453, in __init__
    data["value"],
KeyError: 'value'
biodland commented 1 year ago

Almost same error x) Just other key not found. :)

andersevenrud commented 1 year ago

That's a bit odd. Maybe you could insert this above line 451 in the nexa.py file so I can see what's going on ?

            _LOGGER.info([key, data])

Because I'm suspecting you're getting something that has not come up in the logs shared yet πŸ€”

biodland commented 1 year ago

Trying to see whether or not I can get that logger data, but seems like its not hitting it, or I'm looking at the wrong log files. Looking in the log file inside the config directory.

andersevenrud commented 1 year ago

No worries. I've created another build with some safe-guards when it comes to this area of the code. It will give warnings in the logs if something is off.

Zipped: nexa_bridge_x.zip

biodland commented 1 year ago

Figured out I had to ofc enable the logging for 'info' First time setup of the Home Assistant.

2023-02-11 23:44:44.363 WARNING (MainThread) [homeassistant.config_entries] Config entry 'Nexa Bridge' for nexa_bridge_x integration not ready yet: 'value'; Retrying in background
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] Attempting to load node
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] Attempting to load node
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] ['switchBinary', {'value': False, 'sourceNode': 1017, 'fromWS': False, 'time': '2023-02-11T20:45:59+0100', 'name': 'switchBinary'}]
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] ['methodCall', {'cap': 'switchBinary', 'value': False, 'targetNode': 1017, 'targetRoomId': 0, 'method': 'setValue', 'sourceNode': 1017, 'fromWS': False, 'time': '2023-02-11T20:45:59+0100', 'name': 'methodCall'}]
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] Attempting to load node
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] ['switchBinary', {'value': True, 'sourceNode': 1018, 'fromWS': False, 'time': '2023-02-11T16:18:00+0100', 'name': 'switchBinary'}]
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] ['methodCall', {'cap': 'switchBinary', 'value': False, 'targetNode': 1018, 'targetRoomId': 0, 'method': 'setValue', 'sourceNode': 1018, 'fromWS': False, 'time': '2023-02-03T20:49:21+0100', 'name': 'methodCall'}]
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] Attempting to load node
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] ['switchBinary', {'value': True, 'sourceNode': 1016, 'fromWS': False, 'time': '2023-02-04T11:53:46+0100', 'name': 'switchBinary'}]
2023-02-11 23:44:50.884 INFO (MainThread) [custom_components.nexa_bridge_x.nexa] ['methodCall', {'cap': 'switchBinary', 'targetNode': 1016, 'targetRoomId': 0, 'method': 'turnOn', 'sourceNode': 1016, 'fromWS': False, 'time': '2023-02-04T11:53:46+0100', 'name': 'methodCall'}]
2023-02-11 23:44:50.884 ERROR (MainThread) [custom_components.nexa_bridge_x.nexa] Unexpected error fetching Nexa Bridge X Coordinator data: 'value'
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 239, in _async_refresh
self.data = await self._async_update_data()
File "/config/custom_components/nexa_bridge_x/nexa.py", line 624, in _async_update_data
list(map(lambda n: NexaNode(n, self.legacy), nodes)),
File "/config/custom_components/nexa_bridge_x/nexa.py", line 624, in <lambda>
list(map(lambda n: NexaNode(n, self.legacy), nodes)),
File "/config/custom_components/nexa_bridge_x/nexa.py", line 455, in __init__
data["value"],
KeyError: 'value'

Testing your latest code now.

andersevenrud commented 1 year ago

Ah, so it's the methodCall thing. That explains things πŸ‘

andersevenrud commented 1 year ago

This release ignores anything like that and no longer spams the log: nexa_bridge_x.zip

biodland commented 1 year ago

image This seems promising. :)

andersevenrud commented 1 year ago

Aww yiss🍿 You should get the values, but I'm very interested to see if you can control your switchers/dimmers and whatnot as well with the Bridge2 API endpoints 😁

biodland commented 1 year ago

Seems like there is no control, will try to dig in and test some with postman for controlling with the api.

biodland commented 1 year ago

Dimmer Z-Wave device is also added as a light sensor, meaning it is only reading value it seems. No way to control the dim %

image

andersevenrud commented 1 year ago

Really appreciate it :)

I was doing some reverse-engineering by using the Web UI that's accessible on the Bridge to when you access the IP directly. Maybe that's possible on the Bridge1 as well ? It had basic features for controls.

Dimmer Z-Wave device is also added as a light sensor

This is actually intentional. I wonder if I should add a config option for it. I just found it handy to have a sensor in addition to a control.

No way to control the dim %

That's def not intentional. I have a couple of dimmers at home, and they only appear as a light because they don't have the "binary" capability, only "level" (i.e.: 0-100%). Theoretically that should not have any issues, but I'm going to do some investigation on my end.

andersevenrud commented 1 year ago

Theoretically that should not have any issues, but I'm going to do some investigation on my end.

Tested by forcing my dimmers to become switches as well, and I got both πŸ€”

Is it somewhere in the "Entities" tab disguised as something else perhaps ?

biodland commented 1 year ago

I exported the postman collection.

NexaBridge.postman_collection.zip

I see how it was now, it added the "lights" under an other group. Which only was acceptable under the right page to show. Still new to this. image

andersevenrud commented 1 year ago

I see how it was now, it added the "lights" under an other group. Which only was acceptable under the right page to show. Still new to this.

Ah, I see. It took me a while to get to grips with this as well :)

andersevenrud commented 1 year ago

Also, I see now that I should disable the power meter stuff so that "Nexa Bridge" card doesn't just display garbage. I tried infer the stuff from the logs you shared, but obviously I missed something there :)

andersevenrud commented 1 year ago

Here's without that stuff. NB: Might have to remove the entities manually now that they have data on them (and might just show unavailable from now on).

nexa_bridge_x.zip

Looking forward to what you find out with the controls 😁

andersevenrud commented 1 year ago

Actually, I maybe think what's needed here.

The node_call method on line 344 (nexa.py) sends the command with "capability". I suspect this might need to be "name" because this has been the case elsewhere. Unless the actual endpoint does not actually exists that is.

andersevenrud commented 1 year ago

Meanwhile I've been trying to find some information about this on the web, but there's basically zero. Even for the X bridge (which is why I even started writing a document about all of this).

I'm kind of bummed about that, but figure the solution for this gotta be simple considering how similar the APIs are.

I suspect this might need to be "name" because this has been the case elsewhere

Or maybe even "cap" which is used some places in the new API. It's very inconsistent.

One thing I noticed is that methodCall thing that's on your nodes, which is not on Bridge2. That has the cap on it.

Really hope it's that simple. Or even that the endpoint is called methodCall instead of call, or a combination of all of this πŸ˜…

. I was doing some reverse-engineering by using the Web UI that's accessible on the Bridge to when you access the IP directly. Maybe that's possible on the Bridge1 as well ? It had basic features for controls.

Is this available on your device btw ? If so you can open the browser dev tools to capture network calls to easily see how it's done. If you don't know how to work with this tool I can help out (on discord or something like that even),

biodland commented 1 year ago

Sorry for the late reply. A session in Discord or similar would probably be great at some time.

Opening up the web page locally and clicking the actions. It uses the call endpoint (which is also mentioned in the API doc from Nexa).

/v1/nodes/[nodeId]/call

With the payload of what capability to set value to.

{"cap":"switchBinary","method":"turnOn"} for a switch

Funny story tho, dimming units does not appear to work from the web interface. The button on WebUI does nothing at all. Power On and off does work and calls the above.

dimming switch has the following body for setting dimming level.

{"cap": "switchLevel","value": 0.6}

andersevenrud commented 1 year ago

With the payload of what capability to set value to.

Ah, so there's some kind of special case for buttons here in the old API. That's so weird.

Funny story tho, dimming units does not appear to work from the web interface. The button on WebUI does nothing at all. Power On and off does work and calls the above.

Hm. Does this work from the Nexa app ?

biodland commented 1 year ago

Yes. It works from the app. And it works if I use postman with the payload mentioned aboved with "cap" set to switchLevel and with a value between 0 and 1 (0-100%) :)

biodland commented 1 year ago

Just a side note, it also works from Google Home (I have tried to use Google Home) as well, but due to the high delay I want to use something not cloud based, therefor HA

andersevenrud commented 1 year ago

Yes. It works from the app. And it works if I use postman with the payload mentioned aboved with "cap" set to switchLevel and with a value between 0 and 1 (0-100%) :)

So only not from the web UI. Now that's a fun one :D Hopefully this means that it works from HA.

Well, then I think there's only one thing left to fix on my end and I can make a proper release. I'll fix that right after I finish work :)

biodland commented 1 year ago

Perfect! Will hopefully be available to do a test in the evening if you get a RC ready.