Hi
As it might interest other users in Swiss Germany or Germany to unlock their home automation system, here is a link on how to interface Spline / Innoxel / Zidatech systems with Home-Assistant:
https://www.domedia.net/?p=1761&lang=en
Vincèn
Hi
As it might interest other users in Swiss Germany or Germany to unlock their home automation system, here is a link on how to interface Spline / Innoxel / Zidatech systems with Home-Assistant:
https://www.domedia.net/?p=1761&lang=en
Vincèn
Interesting if it’s as easy with car’s canbus. I have esphome device in car for voltage and temperature readings, I’ll order this adapter and update in few weeks.
I don’t think it’s as easy for cars as there are some standardised protocols for cars but you can have a look at that that works (I use it in my own traditional car not electric): WiCAN Pro | Crowd Supply
I have exactly the same Innoxel system, the same age, and the same need to connect it to Home Assistant. Until now, I had solved this via Innoxel Master - RTI XP6s - MQTT Driver - Home Assistant. Your post here is like Christmas for me. I’ve already done a few projects with ESP32, so I immediately ordered the CAN bus module
I hope everything works out. I’ve only skimmed the article, but I’ll try to implement it as soon as possible. Thank you.
Do you speak german?
Happy the article can help
For the interface you can use that that is nicer and better to fit in electrical panel: Industrial ESP32 IoT Programmable Controller | RS485, CAN, Ethernet - DFRobot and it’s also ESP based and same hw for can interface than the one used in article ![]()
I don’t speak german, only english and French !
Thank you very much. I’ll give it a try. What I have in my Innoxel installation is also a weather station plus lots of buttons with status LEDs, IR receivers, and a NoxIn module. Was none of that available when you implemented it during installation? That makes it difficult to completely omit the NoxMaster.
The Inoxel installation we worked on had of course a weather station linked to it through a serial adapter and data is quite easy to read on the CAN bus, same for IR receiver.
For the Noxin module what is it ? have a picture of it or documentation ?
Nice. Its this one. Only Inputs.
Yes, my weather station is also connected to the CAN bus via an RS485 CAN module from Innoxel. I’ll see how far I can get. I’ve just ordered the industrial CAN device you mentioned ![]()
And something like this for every button with a temperature sensor, some with an IR receiver and connected room controller for the heating.
ah yeah the input boxes you can handle them without problem with codes in the blog article ![]()
Yeah exactly same configuration as we had, we handled the buttons without problem. The only thing we didn’t find out at that time was how to convert temperature (sensor returns a weirdo value but should not be a big deal to find out how to “interpret” it ![]()
I am excited and will report back. Is all this information on website from you free of charge, or is there more that can be purchased? I saw that you have a company. If I hadn’t found your information, I would have considered replacing the Noxmaster controller with the latest Innoxel Master 3, which would have had a REST API.
All the information is in the blog post and work has been sponsored by one of our customer here in Switzarland that wanted to find out if it could be interfaced with HA, and eventually in future replace the Inoxel system fully by HA without changing straight all CAN modules. He wanted also to get control on his home and automation system (get rid of the fully closed and over engineered Inoxel system not even speaking overpriced).
If you don’t succeed to get hands on your system or needs assistance, we would be happy to help you as a paying job.
I understand but then it would keep you in a fully closed system that you have no control on it and if you need to interface systems better to interface straight with CAN devices from HA then pass through a closed system out of control ![]()
The DFRobot Edge101 Industrial ESP32 arrived today, which is good. Do you have a device like this too?
I’m not sure about the GPIOS. I have this part for canbus in ESPHome Builder, similar to your example, but with different GPIOS according to the circuit diagram.
As soon as I connect the CAN bus to it, the device freezes
But it’s just enough to display logs from the CAN. Strange.
canbus:
- platform: esp32_can
tx_pin: GPIO32
rx_pin: GPIO35
can_id: 4
bit_rate: 100kbps
use_extended_id: false
on_frame:
- can_id: 0x000
can_id_mask: 0x000
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD(“can id 0x42B ”, “%s”, &b[0] );
I think the problem is that too much is happening on the CAN bus and too much has to be logged.
Your code looks all good for me. You’ll get a lot of noise all the time as the Inoxel system is very badly managed and the weather station floods bus permanently (you’ll see in ESPHome logs from time to time a message stating some messages have been lost but it doesn’t impair it to work
To help debug I strongly advise to disconnect temporarily the weather station (you can do that easily on the RS-484/CAN interface). You’ll discover suddenly the bus is lot more quiet ![]()
Be careful when you connect the dfrobot to the can bus to respect polarity so H on H and L on L or you are going to sort of shortcut the CAN bus.
I found the first address, still with the weather station connected.
This is the status feedback from a NOXin-0800h 8-channel module.
It only works fast enough if you omit logging on the dfrobot edge101.
It works very well, but I don’t know yet if it’s the right thing to do.
Example:
switch:
- platform: template
id: innoxel_relais_217_k0
name: “Innoxel Relay 0x217 Channel 0”
optimistic: true
turn_on_action: []
turn_off_action: []
- platform: template
id: innoxel_relais_217_k1
name: “Innoxel Relay 0x217 Channel 1”
optimistic: true
turn_on_action: []
turn_off_action: []
- platform: template
id: innoxel_relay_217_k2
name: “Innoxel Relay 0x217 Channel 2”
optimistic: true
turn_on_action: []
turn_off_action: []
- platform: template
id: innoxel_relay_217_k3
name: “Innoxel Relay 0x217 Channel 3”
optimistic: true
turn_on_action: []
turn_off_action: []
- platform: template
id: innoxel_relay_217_k4
name: “Innoxel Relay 0x217 Channel 4”
optimistic: true
turn_on_action: []
turn_off_action: []
- platform: template
id: innoxel_relais_217_k5
name: “Innoxel Relay 0x217 Channel 5”
optimistic: true
turn_on_action: []
turn_off_action: []
- platform: template
id: innoxel_relay_217_k6
name: “Innoxel Relay 0x217 Channel 6”
optimistic: true
turn_on_action: []
turn_off_action: []
- platform: template
id: innoxel_relay_217_k7
name: “Innoxel Relay 0x217 Channel 7”
optimistic: true
turn_on_action: []
turn_off_action: []
# CAN bus
canbus:
- platform: esp32_can
tx_pin: GPIO32
rx_pin: GPIO35
can_id: 4
bit_rate: 100kbps
use_extended_id: false
on_frame:
- can_id: 0x217
can_id_mask: 0x7FF
then:
- lambda: |-
if (x.size() < 2) return;
static uint8_t last_b0 = 0xFF;
static uint8_t last_b1 = 0xFF;
const uint8_t b0 = x[0];
const uint8_t b1 = x[1];
if (b0 != 0x03) return;
if (b0 == last_b0 && b1 == last_b1) return;
last_b0 = b0;
last_b1 = b1;
const bool k0_on = (b1 & 0x01) != 0;
const bool k1_on = (b1 & 0x02) != 0;
const bool k2_on = (b1 & 0x04) != 0;
const bool k3_on = (b1 & 0x08) != 0;
const bool k4_on = (b1 & 0x10) != 0;
const bool k5_on = (b1 & 0x20) != 0;
const bool k6_on = (b1 & 0x40) != 0;
const bool k7_on = (b1 & 0x80) != 0;
id(innoxel_relais_217_k0).publish_state(k0_on);
id(innoxel_relais_217_k1).publish_state(k1_on);
id(innoxel_relais_217_k2).publish_state(k2_on);
id(innoxel_relais_217_k3).publish_state(k3_on);
id(innoxel_relais_217_k4).publish_state(k4_on);
id(innoxel_relais_217_k5).publish_state(k5_on);
id(innoxel_relais_217_k6).publish_state(k6_on);
id(innoxel_relais_217_k7).publish_state(k7_on);
type or paste code here
I am now able to switch all Innoxel Switch modules with Home Assistant and ESPHome via a DFRobot Edge 101. However, it works differently than in your description.
The CAN addresses of the modules match your description. O1 = 0x203, O2 = 0x205, always with an address in between that is not used. However, these addresses are only for status in my case.
b1 & 0x01 → checks bit 0 (0000 0001) → channel 0
b1 & 0x02 → checks bit 1 (0000 0010) → channel 1
b1 & 0x04 → checks bit 2 (0000 0100) → channel 2
b1 & 0x08 → checks bit 3 (0000 1000) → channel 3
b1 & 0x10 → checks bit 4 (0001 0000) → channel 4
b1 & 0x20 → checks bit 5 (0010 0000) → channel 5
b1 & 0x40 → checks bit 6 (0100 0000) → channel 6
b1 & 0x80 → checks bit 7 (1000 0000) → channel 7
However, a channel of a module is switched via a different address. Switching a channel then works as follows, for example:
- id: o11_k0_toggle
then:
- canbus.send: { can_id: 0x588, data: [0x00, 0x0B] }
- delay: 5ms
- canbus.send: { can_id: 0x588, data: [0x00, 0x0F] }
However, the address for switching is individual; no pattern can be identified.
Nevertheless, I was able to find them all by looking at what happens around the module status address when switching.
I therefore think that your description of switching using an S and C command works in conjunction with an AMX controller that responds to it,
but not in a pure Innoxel setup. I can see these S and C commands even when I trigger a command directly from the Innoxel programming software,
but when I send it to the module’s status address using ESPHome, it does not respond.
I’m going to take a look at the dimmers now.
Here is the Example YAML file for the DFRobot Edge 101 for ESPHome with LAN and CAN 100kbit, including the status and switch for Innoxel (individual).
esphome:
name: edge101-can
friendly_name: Edge101-CAN
# Beim Boot Schaltbefehle erst nach 10s erlauben
on_boot:
priority: -10
then:
- delay: 10s
- lambda: |-
id(can_commands_enabled) = true;
esp32:
board: esp32dev
framework:
type: arduino
logger:
level: INFO
logs:
canbus: WARN # internen CAN-Debug-Spam dämpfen
api:
encryption:
key: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ota:
- platform: esphome
password: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# ---------- Start-Sperre für Schaltbefehle ----------
globals:
- id: can_commands_enabled
type: bool
restore_value: no
initial_value: 'false'
# ---------- ETHERNET (Edge101) ----------
ethernet:
type: IP101
mdc_pin: GPIO4
mdio_pin: GPIO13
clk:
pin: GPIO0
mode: CLK_EXT_IN
phy_addr: 1
power_pin: GPIO2
# ---------- CAN-Bus ----------
canbus:
- platform: esp32_can
tx_pin: GPIO32
rx_pin: GPIO35
can_id: 4
bit_rate: 100kbps
use_extended_id: false
on_frame:
# Status-Frames O0 (0x201)
- can_id: 0x201
can_id_mask: 0x7FF
then:
- lambda: |-
if (x.size() < 2) return;
const uint8_t b0 = x[0];
const uint8_t b1 = x[1];
if (b0 != 0x03) return;
id(o0_k0).publish_state(b1 & 0x01);
id(o0_k1).publish_state(b1 & 0x02);
id(o0_k2).publish_state(b1 & 0x04);
id(o0_k3).publish_state(b1 & 0x08);
id(o0_k4).publish_state(b1 & 0x10);
id(o0_k5).publish_state(b1 & 0x20);
id(o0_k6).publish_state(b1 & 0x40);
id(o0_k7).publish_state(b1 & 0x80);
# Status-Frames O10 (0x215)
- can_id: 0x215
can_id_mask: 0x7FF
then:
- lambda: |-
if (x.size() < 2) return;
const uint8_t b0 = x[0];
const uint8_t b1 = x[1];
if (b0 != 0x03) return;
id(o10_k0).publish_state(b1 & 0x01);
id(o10_k1).publish_state(b1 & 0x02);
id(o10_k2).publish_state(b1 & 0x04);
id(o10_k3).publish_state(b1 & 0x08);
id(o10_k4).publish_state(b1 & 0x10);
id(o10_k5).publish_state(b1 & 0x20);
id(o10_k6).publish_state(b1 & 0x40);
id(o10_k7).publish_state(b1 & 0x80);
# ---------- SCRIPTS: Toggle-Pulse ----------
script:
# ------- O0 -------
# o0_k0: (noch kein Schaltframe bekannt)
- id: o0_k1_toggle
then:
- canbus.send: { can_id: 0x59E, data: [0x00, 0x07] }
- delay: 5ms
- canbus.send: { can_id: 0x59E, data: [0x00, 0x0F] }
# o0_k2: (noch kein Schaltframe bekannt)
- id: o0_k3_toggle
then:
- canbus.send: { can_id: 0x53A, data: [0x00, 0x0E] }
- delay: 5ms
- canbus.send: { can_id: 0x53A, data: [0x00, 0x0F] }
# o0_k4..k7: (noch kein Schaltframe bekannt)
# ------- O10 -------
- id: o10_k0_toggle
then:
- canbus.send: { can_id: 0x52C, data: [0x00, 0x0D] }
- delay: 5ms
- canbus.send: { can_id: 0x52C, data: [0x00, 0x0F] }
- id: o10_k1_toggle
then:
- canbus.send: { can_id: 0x52C, data: [0x00, 0x0B] }
- delay: 5ms
- canbus.send: { can_id: 0x52C, data: [0x00, 0x0F] }
- id: o10_k2_toggle
then:
- canbus.send: { can_id: 0x52C, data: [0x00, 0x07] }
- delay: 5ms
- canbus.send: { can_id: 0x52C, data: [0x00, 0x0F] }
# o10_k3: (noch kein Schaltframe bekannt)
- id: o10_k4_toggle
then:
- canbus.send: { can_id: 0x52E, data: [0x00, 0x0D] }
- delay: 5ms
- canbus.send: { can_id: 0x52E, data: [0x00, 0x0F] }
# o10_k5: (noch kein Schaltframe bekannt)
- id: o10_k6_toggle
then:
- canbus.send: { can_id: 0x52E, data: [0x00, 0x0B] }
- delay: 5ms
- canbus.send: { can_id: 0x52E, data: [0x00, 0x0F] }
- id: o10_k7_toggle
then:
- canbus.send: { can_id: 0x52C, data: [0x00, 0x0E] }
- delay: 5ms
- canbus.send: { can_id: 0x52C, data: [0x00, 0x0F] }
# ---------- SWITCHES ----------
switch:
# ------- O0 -------
- platform: template
id: o0_k0
name: "o0_k0"
optimistic: false
turn_on_action: []
turn_off_action: []
- platform: template
id: o0_k1
name: "o0_k1"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o0_k1_toggle
- lambda: |-
id(o0_k1).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o0_k1_toggle
- lambda: |-
id(o0_k1).publish_state(false);
- platform: template
id: o0_k2
name: "o0_k2"
optimistic: false
turn_on_action: []
turn_off_action: []
- platform: template
id: o0_k3
name: "o0_k3"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o0_k3_toggle
- lambda: |-
id(o0_k3).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o0_k3_toggle
- lambda: |-
id(o0_k3).publish_state(false);
- platform: template
id: o0_k4
name: "o0_k4" # Nur Status
optimistic: false
turn_on_action: []
turn_off_action: []
- platform: template
id: o0_k5
name: "o0_k5" # Nur Status
optimistic: false
turn_on_action: []
turn_off_action: []
- platform: template
id: o0_k6
name: "o0_k6" # Nur Status
optimistic: false
turn_on_action: []
turn_off_action: []
- platform: template
id: o0_k7
name: "o0_k7" # Nur Status
optimistic: false
turn_on_action: []
turn_off_action: []
# ------- O10 -------
- platform: template
id: o10_k0
name: "o10_k0"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k0_toggle
- lambda: |-
id(o10_k0).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k0_toggle
- lambda: |-
id(o10_k0).publish_state(false);
- platform: template
id: o10_k1
name: "o10_k1"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k1_toggle
- lambda: |-
id(o10_k1).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k1_toggle
- lambda: |-
id(o10_k1).publish_state(false);
- platform: template
id: o10_k2
name: "o10_k2"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k2_toggle
- lambda: |-
id(o10_k2).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k2_toggle
- lambda: |-
id(o10_k2).publish_state(false);
- platform: template
id: o10_k3
name: "o10_k3" # Nur Status
optimistic: false
turn_on_action: []
turn_off_action: []
- platform: template
id: o10_k4
name: "o10_k4"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k4_toggle
- lambda: |-
id(o10_k4).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k4_toggle
- lambda: |-
id(o10_k4).publish_state(false);
- platform: template
id: o10_k5
name: "o10_k5" # Nur Status
optimistic: false
turn_on_action: []
turn_off_action: []
- platform: template
id: o10_k6
name: "o10_k6"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k6_toggle
- lambda: |-
id(o10_k6).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k6_toggle
- lambda: |-
id(o10_k6).publish_state(false);
- platform: template
id: o10_k7
name: "o10_k7"
optimistic: false
turn_on_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k7_toggle
- lambda: |-
id(o10_k7).publish_state(true);
turn_off_action:
- if:
condition:
lambda: 'return id(can_commands_enabled);'
then:
- script.execute: o10_k7_toggle
- lambda: |-
id(o10_k7).publish_state(false);
Hi
Sorry for the late answer and nice to hear you have been able to do nice progress on your system to take over control with HA.
For code I guess you have done it with some AI as it’s over complicated and quite few non sense stuffs in it
Why not use the one we supply on the blog ? It’s simple and efficient ![]()
Vincèn
Hi Tinu1, I’m Clément, i worked with Vincèn decoding the can bus commands and I wrote a large part of esphome sketch in Vincèn’s blog article.
To help you efficiently, can you show the logs of your DFrobot so we can see together what go through the Can bus ?
I also have to know witch Inoxel device your are working on. Input module ? Relay module ? What is your goal (command via HA ? Feedback ? Control everything via HA or keeping Inoxel gateway by side ?).
Inoxel syteme may have few different command in the bus but it don’t think they are different regarding the gateway (innoxel or amx). The commands we suggest you in Vincèn blog should work everywhere. Especially if you saw them in the logs of your DFrobot.
Yes, that’s right, I created it together with AI. Everything is now working (dimmers, blinds, relays), although perhaps not quite as you described. Your example: Control of ON/OFF circuits, where you send an S6 for turn_on_action and a C6 for turn_off_action to 0x21D. I also have a NoxSwitch-0806 at address 0x21D. When I use the example with S6 and C6, nothing happens at all. Instead, I have to send this, which works: