XBee serial communication

Hi!

I have a ZigBee network for sensors controlled by an XBee S2C module with ZHA component.
I also have another XBee S2C device that controls a relay. In order to open or close the relay I must send arbitrary bytes to it from the XBee module over UART, i.e. 0x65 to open the relay and 0x6F to close it.
According to the XBee specs (https://www.digi.com/resources/documentation/digidocs/pdfs/90002002.pdf) I must issue an API command (0x10 Transmit Request frame) from the coordinator device (which is controlled by HA).

My problem is that I donā€™t know how to do it from HA, and even donā€™t know where start looking.

I guess it is not supported out-of-the-box and I probably need to write a custom module providing the service and probably customize zigpy or zha-quirks or whatever, but I need your advice on where to start, because I am not really familiar with all these stuff yet.

1 Like

How is the relay connected to the xbee and is the second xbee currently joined to the network?

The relay is connected to the xbee with UART ( http://www.tinyosshop.com/index.php?route=product/product&product_id=595 , http://www.tinyosshop.com/datasheet/TOSR0X-T%20User%20Manual.pdf ).
Yes, it has joined the network already.

Ok, Iā€™m not certain that itā€™s possible to send arbitrary bytes through the xbee UART in API mode. It may be, but itā€™s not something I have researched. If it is, you will likely need to build a quirk and probably add the required functionality to zigpy_xbee.

Another option would be to use the DIO pins on the xbee directly, Iā€™m doing that now and am in the process of PRing to zhaquriks https://github.com/dmulcahey/zha-device-handlers/pull/73 This would require you to change relays, Iā€™ve driven 3.3v SSRs directly from the DIO pins without issue.

The other option would be to add a microcontroller and have the device join as a ZHA device. You could then have the microcontroller output the required bytes. This is a project where I am using that method. https://github.com/prairiesnpr/QuadGarageDoorController Itā€™s actually controlling 4 relays, but instead of switching a digital output, you could transmit via the UART.

Thank you!
I can see you are using remote_at_request. I believe I can use tx or tx_explicit to send UART data, without modification of zigpy_xbee.
Besides the quirks, do I need to change anything in HA itself to add a switch or service or sensor based on that quirk?

No, HA will create the appropriate entities based on the endpoints. That quirk looks like this when joined.

just edit the replacement endpoint list to match your relay.

Well, Iā€™ve tried that and it works, I can send any data on my coordinator (API mode) and the end device (xbee transparent mode) just prints it to UART. Here is a quick and dirty code that tests the idea, when I flip a switch on or off, it prints ā€˜dā€™ or ā€˜nā€™: https://github.com/Shulyaka/zha-device-handlers/tree/xbee_dio2

Now, I am thinking on how to do it in a more generic way to be useful for others. Obviously, I donā€™t want to hardcode TOSR0X commands into xbee zhaquirk because xbee could be connected to a wide range of devices. I think of it as a device that has a service, and any module could call the service with arbitrary data. Any switches or sensors communicating to xbee could be built on top of that service.
This is where I got stuck. I donā€™t know how to write a zhaquirk that provides a service. Which device type should I use? Are there any other devices out there that require arbitrary data to be sent by HA?

I feel like I found the right path, will update you as soon as Iā€™m done

I finally made it work as a service. One way communication so far.
Code: https://github.com/Shulyaka/zha-device-handlers/tree/serial_data_cluster
Service: zha.issue_zigbee_cluster_command
Data: {ā€œieeeā€: ā€œ00:13:a2:00:40:f5:2a:beā€, ā€œendpoint_idā€: 232, ā€œcluster_idā€: 17, ā€œcluster_typeā€: ā€œoutā€, ā€œcommandā€: 0, ā€œcommand_typeā€: ā€œserverā€, ā€œargsā€: ā€œHello World!\nā€}
Now I will try to make a switch out of this service.

Iā€™m surprised how things that looked very complicated at first, turned out to be fairly easy!

# http://www.tinyosshop.com/datasheet/TOSR0X-T%20User%20Manual.pdf
input_boolean:
  tosr0x_1_state:
    name: TOSR0X Relay 1 State
  tosr0x_2_state:
    name: TOSR0X Relay 2 State
  tosr0x_3_state:
    name: TOSR0X Relay 3 State
  tosr0x_4_state:
    name: TOSR0X Relay 4 State
  tosr0x_5_state:
    name: TOSR0X Relay 5 State
  tosr0x_6_state:
    name: TOSR0X Relay 6 State
  tosr0x_7_state:
    name: TOSR0X Relay 7 State
  tosr0x_8_state:
    name: TOSR0X Relay 8 State

switch:
  - platform: template
    switches:
      tosr0x_1:
        value_template: "{{ is_state('input_boolean.tosr0x_1_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: e
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_1_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: o
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_1_state
      tosr0x_2:
        value_template: "{{ is_state('input_boolean.tosr0x_2_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: f
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_2_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: p
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_2_state
      tosr0x_3:
        value_template: "{{ is_state('input_boolean.tosr0x_3_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: g
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_3_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: q
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_3_state
      tosr0x_4:
        value_template: "{{ is_state('input_boolean.tosr0x_4_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: h
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_4_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: r
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_4_state
      tosr0x_5:
        value_template: "{{ is_state('input_boolean.tosr0x_5_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: i
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_5_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: s
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_5_state
      tosr0x_6:
        value_template: "{{ is_state('input_boolean.tosr0x_6_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: j
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_6_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: t
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_6_state
      tosr0x_7:
        value_template: "{{ is_state('input_boolean.tosr0x_7_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: k
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_7_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: u
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_7_state
      tosr0x_8:
        value_template: "{{ is_state('input_boolean.tosr0x_8_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: l
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_8_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: v
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_8_state

Now the hard part is the receiving data from the device to get the initial states of the relays. But this part is optional.

Nice, didnā€™t know you could do that with the xbee in API mode. Glad you got it figured out. Sorry for the delay, have been travelling.

To anyone looking for the solution for a similar device, here is my final configuration for TOSR08-T. It can easily be modified for TOSR04-T or TOSR02-T by removing corresponding relays or the temperature sensor if your device doesnā€™t have it (without the ā€œ-Tā€ part). I tested it on TOSR04-T, but I will post the solution for TOSR08-T since it is easier to remove sections than to add.

Supported:

  • Relay control
  • Getting relay states from the device
  • Temperature sensor

Requires a recent version of zhaquircks.

homeassistant:
  customize:
    automation.update_tosr0x_switch:
      hidden: true
    automation.update_tosr0x_temp:
      hidden: true
    automation.update_tosr0x:
      hidden: true
    input_number.tosr0x_temp:
      hidden: true
  customize_glob:
    "input_boolean.tosr0x_*_state":
      hidden: true

logbook:
  exclude:
    entities:
      - automation.update_tosr0x
      - automation.update_tosr0x_temp
      - automation.update_tosr0x_switch

# Internal hidden variables to hold TOSR0X relay state
input_boolean:
  tosr0x_1_state:
    name: TOSR0X Relay 1 State
  tosr0x_2_state:
    name: TOSR0X Relay 2 State
  tosr0x_3_state:
    name: TOSR0X Relay 3 State
  tosr0x_4_state:
    name: TOSR0X Relay 4 State
  tosr0x_5_state:
    name: TOSR0X Relay 5 State
  tosr0x_6_state:
    name: TOSR0X Relay 6 State
  tosr0x_7_state:
    name: TOSR0X Relay 7 State
  tosr0x_8_state:
    name: TOSR0X Relay 8 State

# Internal variable to hold TOSR0X-T temperature
input_number:
  tosr0x_temp:
    name: TOSR0X-T Temperature State
    min: -55
    max: 125
    step: 0.125
    mode: box
    unit_of_measurement: 'Ā°C'
    icon: mdi:temperature-celsius

# Sensor to display the TOSR0X-T temperature
sensor:
  - platform: template
    sensors:
      tosr0x_temperature:
        friendly_name: TOSR0X-T Temperature
        entity_id: input_number.tosr0x_temp
        value_template: "{{ states.input_number.tosr0x_temp.state }}"
        device_class: temperature
        unit_of_measurement: 'Ā°C'

# Switches to represent TOSR0X relays
# http://www.tinyosshop.com/datasheet/TOSR0X-T%20User%20Manual.pdf
switch:
  - platform: template
    switches:
      tosr0x_1:
        friendly_name: TOSR0X Relay 1
        value_template: "{{ is_state('input_boolean.tosr0x_1_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "e["  # First character is to control the switch. Second character ('[') is to request an update of the switch states from the device in order to double confirm
          - service: input_boolean.turn_on  # Update switch state instantly without waiting for the update from the device (aka optimistic mode)
            data:
              entity_id: input_boolean.tosr0x_1_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "o["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_1_state
      tosr0x_2:
        friendly_name: TOSR0X Relay 2
        value_template: "{{ is_state('input_boolean.tosr0x_2_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "f["
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_2_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "p["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_2_state
      tosr0x_3:
        friendly_name: TOSR0X Relay 3
        value_template: "{{ is_state('input_boolean.tosr0x_3_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "g["
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_3_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "q["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_3_state
      tosr0x_4:
        friendly_name: TOSR0X Relay 4
        value_template: "{{ is_state('input_boolean.tosr0x_4_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "h["
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_4_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: "r["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_4_state
      tosr0x_5:
        value_template: "{{ is_state('input_boolean.tosr0x_5_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "i["
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_5_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "s["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_5_state
      tosr0x_6:
        value_template: "{{ is_state('input_boolean.tosr0x_6_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "j["
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_6_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "t["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_6_state
      tosr0x_7:
        value_template: "{{ is_state('input_boolean.tosr0x_7_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "k["
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_7_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "u["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_7_state
      tosr0x_8:
        value_template: "{{ is_state('input_boolean.tosr0x_8_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "l["
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.tosr0x_8_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:40:f5:2a:be
              endpoint_id: 232
              cluster_id: 17
              cluster_type: out
              command: 0
              command_type: server
              args: "v["
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.tosr0x_8_state

automation:
  - alias: update_tosr0x  # Request the temperature and relay states from TOSR0X
    trigger:
      - platform: time_pattern
        minutes: '/5'
      - platform: homeassistant
        event: start
    action:
      service: zha.issue_zigbee_cluster_command 
      data:
        ieee: 00:13:a2:00:40:f5:2a:be
        endpoint_id: 232
        cluster_id: 17
        cluster_type: in
        command: 0
        command_type: server
        args: "a["  # First character is for temperature, second character is for relay states, please see the TOSR0X manual
  - alias: update_tosr0x_switch  # Update relay states upon receiving them from TOSR0X
    trigger:
      platform: event
      event_type: zha_event
      event_data:
        device_ieee: 00:13:a2:00:40:f5:2a:be
        command: receive_data
    condition:
      condition: template
      value_template: "{{ trigger.event.data.args | length == 1 }}"  # A single byte response is probably a relay state update
    action:
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**0) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_1_state
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**1) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_2_state
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**2) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_3_state
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**3) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_4_state
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**4) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_5_state
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**5) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_6_state
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**6) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_7_state
      - service_template: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) | bitwise_and(2**7) %}
          input_boolean.turn_on
          {% else %}
          input_boolean.turn_off
          {% endif %}
        data:
          entity_id: input_boolean.tosr0x_8_state
  - alias: update_tosr0x_temp  # Update temperature upon receiving it from TOSR0X
    trigger:
      platform: event
      event_type: zha_event
      event_data:
        device_ieee: 00:13:a2:00:40:f5:2a:be
        command: receive_data
    condition:
      condition: template
      value_template: "{{ trigger.event.data.args | length == 2 }}"  # A two-byte response is probably a temperature update (2's complement format)
    action:
      service: input_number.set_value
      data_template:
        entity_id: input_number.tosr0x_temp
        value: >
          {% if trigger.event.data.args.encode('latin1').hex() | int(base=16) > 32767 %}
          {{ trigger.event.data.args.encode('latin1').hex() | int(base=16) / 16 - 4096 }}
          {% else %}
          {{ trigger.event.data.args.encode('latin1').hex() | int(base=16) / 16 }}
          {% endif %}

group:
  tosr0x:
    name: TOSR0X
    entities:
      - zha.xbee_xbee_io
      - sensor.tosr0x_temperature
      - switch.tosr0x_1
      - switch.tosr0x_2
      - switch.tosr0x_3
      - switch.tosr0x_4
      - switch.tosr0x_5
      - switch.tosr0x_6
      - switch.tosr0x_7
      - switch.tosr0x_8
2 Likes

Just found the discussion. How do you join the second Xbee to zha network?

Probably you could just enable join in HA interface (http://hassio.local:8123/config/zha/add) and press the commissioning button on the second xbee (pin 20 to ground) once.
If that doesnā€™t work, place the two xbees closer to each other.

Instead of commisioning button you can try XTCU to simulate it or to manually set the parameters. The important are:
CE=0
ZS=2
EE=1
EO=2
NK=0
KY=5a6967426565416c6c69616e63653039

1 Like

Thanks.
Where you take the Encription key? Is it documented somethere? Can you change it?

I got it here: zigpy-xbee/zigpy_xbee/zigbee/application.py at d3dfb11b336e4bbaaaa0abe2d0b7627c6ac07f83 Ā· zigpy/zigpy-xbee Ā· GitHub

It is the default trusted center (usually, the coordinator node) link key documented in zigbee standard. It is used during join to transfer the actual network key, which is randomly generated by the trust center node. It is not used by nodes when they already joined.

You can. Please note however:

  1. The key must be preconfigured on all new nodes joining the network. If all your devices are xbee, then it is not a problem. For less configurable devices such as sensors it could be an issue.
  2. Currently the link key is hardcoded in the zigpy_xbee code. If you overwrite it, you will need to do it after every zigpy_xbee update.
1 Like

Thanks, your information was very useful for me, saved many time.

Thanks, this thread has really helped me. One thing:
Note that the github projects referenced from this and the previous posts have been removed. It took me a while to eventually figure out that you had integrated your work into https://github.com/dmulcahey/zha-device-handlers/

Yes, my changes were accepted upstream, now you donā€™t need anything beyond a recent version of zhaquircks. I probably needed to mention that more explicitly in the post that I marked as a solution.

Thanks for a great solution :grinning:
I struggled to come up with a solution for interfacing from Home Assistant to micropython on XBee3 until I stumbled on this.
I used a similar configuration to control XBee3 micropython which activates Seeed SPDT relays over i2c.

The configuration looks like this - I have just shown one of the four relays:

input_boolean:
  xbee_aaca92_i2c_channel17_relay1_state:
    name: "XBee i2c17 Relay 1 State"

switch:
  - platform: template
    switches:
      xbee_aaca92_i2c_channel17_relay1:
        friendly_name: Pool pump relay
        value_template: "{{ is_state('input_boolean.xbee_aaca92_i2c_channel17_relay1_state', 'on') }}"
        turn_on:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:41:aa:ca:92
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: '{"relay": 1, "status": true}'
          - service: input_boolean.turn_on
            data:
              entity_id: input_boolean.xbee_aaca92_i2c_channel17_relay1_state
        turn_off:
          - service: zha.issue_zigbee_cluster_command
            data:
              ieee: 00:13:a2:00:41:aa:ca:92
              endpoint_id: 232
              cluster_id: 17
              cluster_type: in
              command: 0
              command_type: server
              args: '{"relay": 1, "status": false}'
          - service: input_boolean.turn_off
            data:
              entity_id: input_boolean.xbee_aaca92_i2c_channel17_relay1_state

The micropython on the XBee3 looks as follows (The XBee3 needs to be configured in API mode):

import time
import xbee

from machine import I2C
import ujson
import struct

i2c = I2C(1)

CMD_CHANNEL_CTRL = 0x10
relayState = 0x0

while True:
    # Check if the XBee has any message in the queue.
    received_msg = xbee.receive()
    if received_msg:
        # Get the sender's 64-bit address and payload from the received message.
        sender = received_msg['sender_eui64']
        payload = received_msg['payload']
        try:
            obj = ujson.loads(payload.decode())
            if obj['relay'] < 1 | obj['relay'] > 8:
                print("relay must be an integer from 1 - 8")
            else:
                if obj['status']:
                    relayState |= (1 << obj['relay'] - 1)
                else:
                    relayState &= ~(1 << obj['relay'] - 1)

                cmdBytes = struct.pack("BB", CMD_CHANNEL_CTRL, relayState)
                print(relayState)
                print(cmdBytes)
                i2c.writeto(17, cmdBytes)
                # Send back the same payload to the sender.
                print("Sending back a response...\n")
            # xbee.transmit(sender, payload)
        except ValueError:
            print("Failed to parse json for payload:")
            print(payload)
    else:
        # Wait 100 ms before checking for data again.
        time.sleep(0.2)

I would like to look at using more standard Zigbee protocol message to drive the micropython, but for now I am pleased with the progress.

1 Like