From 384bd54e8b61dad0962a32fd86cc67cfa262d47c Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Thu, 2 Jul 2026 18:46:06 +0200 Subject: [PATCH] =?UTF-8?q?edge-host:=20E3onCAN=20service=20=E2=80=94=20re?= =?UTF-8?q?ad=20the=20E380=20meter=20directly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passive listener (no bus writes, coexists with the single open3e instance) decoding the E380's own CANopen broadcasts: per-phase active/ reactive power, currents + cosPhi, voltages + grid frequency, and the meter's cumulated import/export counters — instead of trusting the VX3's relayed values. Pinned to V0.5.2; ~5 Hz broadcasts throttled to one MQTT update per DID per 10 s; JSON payloads under the open3e/ prefix with the shared broker user. Co-Authored-By: Claude Fable 5 --- ansible/roles/edge-host/defaults/main.yml | 8 +++++ ansible/roles/edge-host/handlers/main.yml | 6 ++++ ansible/roles/edge-host/tasks/main.yml | 31 +++++++++++++++++++ .../edge-host/templates/e3oncan.service.j2 | 31 +++++++++++++++++++ .../roles/edge-host/templates/open3e.env.j2 | 9 ++++-- 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 ansible/roles/edge-host/templates/e3oncan.service.j2 diff --git a/ansible/roles/edge-host/defaults/main.yml b/ansible/roles/edge-host/defaults/main.yml index a395e5aa..869ed23d 100644 --- a/ansible/roles/edge-host/defaults/main.yml +++ b/ansible/roles/edge-host/defaults/main.yml @@ -41,6 +41,14 @@ edge_open3e_devices: vx3: { tx: "0x6a1", dpList: "vx3" } vcmu: { tx: "0x68c" } +# --- E3onCAN: passive E380 energy-meter listener (no bus writes, so it +# coexists with the single open3e instance). The meter broadcasts at +# ~5 Hz; the gap throttles MQTT to one update per DID per interval. +edge_e3oncan_repo: https://github.com/MyHomeMyData/E3onCAN.git +edge_e3oncan_version: 461dcbdf97e8d72d2edb9929c63b39382af6c3a1 # V0.5.2 +edge_e3oncan_gap_seconds: 10 +edge_e3oncan_mqtt_format: "{device}_{didNumber:04d}_{didName}" + # --- Secrets: define these in inventory/group_vars/edge_host/vault.yml # (ansible-vault). See README.md for the kubectl one-liners that read the # current values out of the cluster during migration. diff --git a/ansible/roles/edge-host/handlers/main.yml b/ansible/roles/edge-host/handlers/main.yml index 8962059a..228c7c46 100644 --- a/ansible/roles/edge-host/handlers/main.yml +++ b/ansible/roles/edge-host/handlers/main.yml @@ -17,3 +17,9 @@ name: open3e state: restarted daemon_reload: true + +- name: Restart e3oncan + ansible.builtin.systemd: + name: e3oncan + state: restarted + daemon_reload: true diff --git a/ansible/roles/edge-host/tasks/main.yml b/ansible/roles/edge-host/tasks/main.yml index 37798fc9..12b98b84 100644 --- a/ansible/roles/edge-host/tasks/main.yml +++ b/ansible/roles/edge-host/tasks/main.yml @@ -152,3 +152,34 @@ enabled: true state: started daemon_reload: true + +# --- E3onCAN: direct E380 meter reads (passive; per-review decision not +# to trust the VX3's relayed meter values). Same broker user as open3e; +# topics land under the same open3e/ prefix as e380__. +- name: Check out E3onCAN (pinned) + ansible.builtin.git: + repo: "{{ edge_e3oncan_repo }}" + dest: "{{ edge_base_dir }}/e3oncan/src" + version: "{{ edge_e3oncan_version }}" + notify: Restart e3oncan + +- name: Create E3onCAN venv with requirements + ansible.builtin.pip: + requirements: "{{ edge_base_dir }}/e3oncan/src/requirements.txt" + virtualenv: "{{ edge_base_dir }}/e3oncan/venv" + virtualenv_command: python -m venv + notify: Restart e3oncan + +- name: Deploy e3oncan systemd unit + ansible.builtin.template: + src: e3oncan.service.j2 + dest: /etc/systemd/system/e3oncan.service + mode: "0644" + notify: Restart e3oncan + +- name: Enable e3oncan + ansible.builtin.systemd: + name: e3oncan + enabled: true + state: started + daemon_reload: true diff --git a/ansible/roles/edge-host/templates/e3oncan.service.j2 b/ansible/roles/edge-host/templates/e3oncan.service.j2 new file mode 100644 index 00000000..a2172f6d --- /dev/null +++ b/ansible/roles/edge-host/templates/e3oncan.service.j2 @@ -0,0 +1,31 @@ +# Managed by ansible (roles/edge-host). +# PASSIVE listener for the E380 energy meter's CANopen broadcasts — no +# writes on the bus, so it may run alongside the (single) open3e +# instance. Reads the meter directly instead of trusting the VX3's +# relayed values. +[Unit] +Description=E3onCAN E380 energy-meter CAN listener (read-only) +BindsTo=sys-subsystem-net-devices-{{ edge_can_interface }}.device +After=sys-subsystem-net-devices-{{ edge_can_interface }}.device network-online.target +Wants=network-online.target + +[Service] +User=open3e +EnvironmentFile={{ edge_base_dir }}/open3e/open3e.env +WorkingDirectory={{ edge_base_dir }}/e3oncan +# Journald-friendly (python block-buffers pipes; lost on restarts). +Environment=PYTHONUNBUFFERED=1 +ExecStart={{ edge_base_dir }}/e3oncan/venv/bin/python {{ edge_base_dir }}/e3oncan/src/E3onCANcollect.py \ + -c {{ edge_can_interface }} \ + -dev e380 \ + -g {{ edge_e3oncan_gap_seconds }} \ + -m {{ edge_mqtt_host }}:{{ edge_mqtt_port }}:{{ edge_open3e_mqtt_topic }} \ + -muser {{ edge_open3e_mqtt_user }} \ + -mpass ${OPEN3E_MQTT_PASSWORD} \ + -mfstr "{{ edge_e3oncan_mqtt_format }}" \ + -j +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/edge-host/templates/open3e.env.j2 b/ansible/roles/edge-host/templates/open3e.env.j2 index 5eb4f8c1..b533369f 100644 --- a/ansible/roles/edge-host/templates/open3e.env.j2 +++ b/ansible/roles/edge-host/templates/open3e.env.j2 @@ -1,4 +1,7 @@ -# Managed by ansible (roles/edge-host). Read by open3e.service. -# Full user:password pair as one variable so the unit can pass it as a -# single -muser argument without exposing it in the unit file. +# Managed by ansible (roles/edge-host). Read by open3e.service and +# e3oncan.service. +# Full user:password pair as one variable so the open3e unit can pass it +# as a single -muser argument without exposing it in the unit file; +# e3oncan takes user and password separately (-muser/-mpass). OPEN3E_MUSER={{ edge_open3e_mqtt_user }}:{{ edge_open3e_mqtt_password }} +OPEN3E_MQTT_PASSWORD={{ edge_open3e_mqtt_password }} -- 2.54.0