Tak dobře, přemluvili jste mě
Berte to fakt jako pracovní verzi, některé úpravy by šli určitě vyřešit lépe, ale funguje to a v pythonu jsem začátečník.
Celá funkčnost vychází z toho, že MOXA je v konfiguraci TCO CLient a skript je upraven na tuto komunikaci. Je možné, že pokud není potřeba připojení na Studer portál, stačilo by nastavit MOXA do modu TCP server a pak bude skript fungovat bez níže uvedených změn, ale to nemám otestované.
Provozuji to na Raspberry PI 4B 8GB doplněný o pasivní chlazení (to jsem musel upravit, ale to je jiný příběh) v krabičce s displejem Waveshare 7" 1024 x 600. Data se ukládají na SSD disk WD 128GB připojený přes USB3.
Skript mám nastavený, aby se pustil při startu skrze service -
https://www.youtube.com/watch?v=nvx9jJhSELQ
1. Konfigurace MOXA je stejná jako tady -
https://forum.mypower.cz/viewtopic.php?t=6422 - pokud nechcete připojení na Studer portál, tak vynecháte generování GUID a v části ,,Operating Settings" nebude adresa na studer-innotec. Do Destination IP address 1 nebo 2 vyplníte IP adresu zařízení, které bude komunikovat s MOXA resp. kde poběží skript vyčítání hodnot. (v mém případě 192.168.0.199 je adresa Raspberry)
- Moxa.jpg (37.35 KiB) Zobrazeno 4180 x
2. Instalace XcomLAN -
https://github.com/Mustafa-Abu-Ghazy/XcomLAN
- instaloval jsem verzi s Thingsboard, což je vlastně MQTT
- instalace proběhla v pohodě, ale jak autor píše, bylo potřeba dva soubory baseframe.cpython-39-aarch64-linux-gnu.so a property.cpython-39-aarch64-linux-gnu.so přesunout z root kam se instalují package do dresáře sin\scom - v mém případě ,,\usr\local\lib\python3.9\dist-packages\" do ,,\usr\local\lib\python3.9\dist-packages\sino\scom\"
- skript se spouští souborem example.py, který je potřeba doplnit o konfiguraci :
na radku 36 je adresa MQTT serveru. Pokud je na stejném zařízení, může se nechat "localhost".
Já tam dal IP adresu -
Kód: Vybrat vše
THINGSBOARD_SERVER = os.environ.get("THINGSBOARD_SERVER", "192.168.0.199")
na radku 39 jsou různé možnosti připojení na NODE MQTT (COM port, IP socket). V tomto případě je zápis tento :
Kód: Vybrat vše
NODES_LIST = {
"N01": {"interface": "socket://192.168.0.199:4001",
"longitude": 0,
"latitude": 0
}
na radku 51 je připojení na MOXA krabičku (COM port, IP socket). V tomto případě je zápis tento :
Kód: Vybrat vše
XCOM_LAN_CONFIG_TEMPLATE = {
"scom": {
"interface": "socket://192.168.0.199:4001",
"baudrate": 115200,
"parity": serial.PARITY_NONE
},
"scom-device-address-scan": {
DeviceType.XTENDER: XT_DEVICE_ID_DEFAULT_SCAN_RANGE,
DeviceType.VARIO_TRACK: VT_DEVICE_ID_DEFAULT_SCAN_RANGE,
DeviceType.VARIO_STRING: VS_DEVICE_ID_DEFAULT_SCAN_RANGE,
DeviceType.RCC: RCC_DEVICE_ID_DEFAULT_SCAN_RANGE,
DeviceType.BSP: BSP_DEVICE_ID_DEFAULT_SCAN_RANGE
}
jak píše v návodu, je potřeba vytvořit soubor .env nebo přejmenovat .env.template a upravit hodnoty uvnitř tohoto konfiguračního souboru. Podle hodnoty READINGS_DELAY_IN_SECONDS se určuje frekvence vyčítání hodnot. Tady časem přidám i možnost username+heslo pro MQTT, aby nebylo natvrdo v kódu.
3.Změny ve skriptech :
home\<user>\XcomLAN\XcomLAN\node_manager\node_manager.py
radek 222 pridano
Kód: Vybrat vše
self._thread_sleep_interval(600) # pocet sekund nez se spusti dalsi kontrola pripojenych zarizeni.
home\<user>\XcomLAN\XcomLAN\thingsboard\thingsboard_client.py
radek 33 zakomentovano :
Kód: Vybrat vše
# super(ThingsBoardClient, self).__init__(host, token, port, gateway, quality_of_service)
a pod to přidána konfigurace pripojeni na MQTT pomocí uživatelského jména a hesla :
Kód: Vybrat vše
super(ThingsBoardClient, self).__init__(host, port, "<username>","<heslo>",gateway, quality_of_service)
Např.
Kód: Vybrat vše
super(ThingsBoardClient, self).__init__(host, port, "mqttbroker","heslo",gateway, quality_of_service)
home\<user>\XcomLAN\XcomLAN\scom.py
radek 11 pridano import socket
radek 52 az 55 nahrazen za toto :
Kód: Vybrat vše
socket_sep1 = com_port.find(":")+3
socket_sep2 = com_port.find(":",socket_sep1)
socket_len = len(com_port)
host_moxa = com_port[socket_sep1:socket_sep2] # "192.168.0.199"
socket_port = socket_sep2 - socket_len + 1
port_moxa = int(com_port[socket_port:]) # 4001
if "://" in com_port:
# self._ser = serial.serial_for_url(url=com_port, baudrate=baudrate, parity=parity)
moxa_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
moxa_socket.bind((host_moxa,port_moxa))
moxa_socket.listen(2)
clientsocket, address = moxa_socket.accept()
self._ser = clientsocket
else:
self._ser = serial.Serial(port=com_port, baudrate=baudrate, parity=parity)
Kdo nemá připojení na Studer portál, následující změnu dělat nemusí.
home\<user>\XcomLAN\XcomLAN\device\base_device.py
na radek 17 pridat
na radek 174 pridano
Kód: Vybrat vše
if len(value) > 10:
# logging.info("Delka je "+str(len(value)) + " / " + str(value))
time.sleep(15)
value = self._read_user_info_by_parameter_id(user_info_id)
# logging.info("Nova delka je "+str(len(value)) + " / " + str(value))
Další změny jsou přímo v nainstalovaných packages scom (cesta záleží jaký máte systém, verzi atd.)
\usr\local\lib\python3.9\dist-packages\sino\scom\scom.py
na řádek 7 přidat :
řádky 73 a 74 nahradit tímto :
Kód: Vybrat vše
# self.set_rx_timeout(rx_timeout_in_seconds) # Set time to wait for the response
# self._ser.write(buffer)
self._ser.send(buffer)
řádek 100 nahradit tímto :
Kód: Vybrat vše
# rx_data_size = self._ser.in_waiting # Check how many bytes available
rx_data_size, _, _ = select.select([self._ser], [], [])
řádek 106 a 107 nahradit tímto :
Kód: Vybrat vše
# self._rxBuffer += bytearray(self._ser.read(rx_data_size))
# byte_array = bytearray()
data_recv = self._ser.recv(1024)
# byte_array.extend(data_recv)
# self._rxBuffer += byte_array
# self._rxBuffer += data_recv
self._rxBuffer = data_recv
# Update size counter
# rx_data_size_total += rx_data_size
rx_data_size_total += len(data_recv)
\usr\local\lib\python3.9\dist-packages\sino\scom\frame.py
této změny si nejsem vědom, že bych dělal, množná se dělá při instalaci, ale pro jistotu ji uvádím
na řádek 46 přidat :
Kód: Vybrat vše
def new_method(self, rx_buffer):
return rx_buffer[10:10 + 2]
Pro jistotu přikládám všechny změněné soubory i s cestou.
A jak to celé funguje :
Skript nejdříve nalezne připojená zařízení (Variotrack, XTM, BSP atd.), uloží si jejich adresy a v pravidelných intervalech (READINGS_DELAY_IN_SECONDS) vyčítá hodnoty ze zařízení podle kódů, které jsou v example.py od řádku 80 až 99. Jsou tam jen některé vybrané kódy a v případě potřeby stačí nový kód jen přidat. Hodnoty, které vrátí skript následně pošle na MQTT server, který je zpracuje a případně uloží do databáze atd. Význam těch kódů je na strankach Studer - Download - OpenStuder - communication protocol xcom 232i , konkrétně v Technical specification - Xtender serial protocol appendix - 1.6.32.pdf .
Druhým cyklem je kontrola dostupnmých zařízení Studer, kde je nastaveno každých 10 minut - node_manager.py .
Pro úplnost výpis z logu, jak by to mělo vypadat :
Kód: Vybrat vše
mrazik@raspberrypi:~/XcomLAN $ /usr/bin/env /bin/python /home/mrazik/.vscode/extensions/ms-python.python-2023.4.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher 50243 -- /home/mrazik/XcomLAN/example.py
2023-05-13 20:41:05.887 - XcomLAN.node_manager.node_manager:N01 - INFO - NodeManager thread running...
2023-05-13 20:41:05.888 - XcomLAN.node_manager.node_manager:N01 - INFO - Searching devices in group 'DeviceType.XTENDER'...
2023-05-13 20:41:06.088 - XcomLAN.node_manager.node_manager:N01 - INFO - Found device on address: 101
2023-05-13 20:41:06.189 - XcomLAN.node_manager.node_manager:N01 - INFO - Found device on address: 102
2023-05-13 20:41:06.912 - XcomLAN.node_manager.node_manager:N01 - INFO - Found new studer device: DeviceType.XTENDER #101
2023-05-13 20:41:06.929 - XcomLAN.node_manager.node_manager:N01 - INFO - Found new studer device: DeviceType.XTENDER #102
2023-05-13 20:41:06.930 - XcomLAN.node_manager.node_manager:N01 - INFO - Searching devices in group 'DeviceType.VARIO_TRACK'...
2023-05-13 20:41:07.031 - XcomLAN.node_manager.node_manager:N01 - INFO - Found device on address: 300
2023-05-13 20:41:07.156 - XcomLAN.node_manager.node_manager:N01 - INFO - Found device on address: 301
2023-05-13 20:41:07.257 - XcomLAN.node_manager.node_manager:N01 - INFO - Found device on address: 302
2023-05-13 20:41:08.588 - XcomLAN.node_manager.node_manager:N01 - INFO - Found new studer device: DeviceType.VARIO_TRACK #300
2023-05-13 20:41:08.606 - XcomLAN.node_manager.node_manager:N01 - INFO - Found new studer device: DeviceType.VARIO_TRACK #301
2023-05-13 20:41:08.621 - XcomLAN.node_manager.node_manager:N01 - INFO - Found new studer device: DeviceType.VARIO_TRACK #302
2023-05-13 20:41:08.622 - XcomLAN.node_manager.node_manager:N01 - INFO - Searching devices in group 'DeviceType.VARIO_STRING'...
2023-05-13 20:41:10.233 - XcomLAN.node_manager.node_manager:N01 - WARNING - No devices in group 'DeviceType.VARIO_STRING' found
2023-05-13 20:41:10.233 - XcomLAN.node_manager.node_manager:N01 - INFO - Searching devices in group 'DeviceType.RCC'...
2023-05-13 20:41:10.334 - XcomLAN.node_manager.node_manager:N01 - WARNING - No devices in group 'DeviceType.RCC' found
2023-05-13 20:41:10.334 - XcomLAN.node_manager.node_manager:N01 - INFO - Searching devices in group 'DeviceType.BSP'...
2023-05-13 20:41:10.435 - XcomLAN.node_manager.node_manager:N01 - INFO - Found device on address: 601
2023-05-13 20:41:10.440 - XcomLAN.node_manager.node_manager:N01 - INFO - Found new studer device: DeviceType.BSP #601
2023-05-13 20:42:06.557 - tb_device_mqtt - INFO - connection SUCCESS
2023-05-13 20:42:06.568 - XcomLAN.thingsboard.thingsboard_client:Studer Xcom-LAN Node:N01 - INFO - Send to Node: N01 Telemetry Content: {'ts': 1684003325950, 'values': {'VTALLI11043': -0.0012149810791015625}} Sending Telemetry Result Status: True
2023-05-13 20:42:08.501 - XcomLAN.thingsboard.thingsboard_client:Studer Xcom-LAN Node:N01 - INFO - Send to Node: N01 Telemetry Content: {'ts': 1684003325952, 'values': {'BSPI7030': 48.84375, 'BSPI7031': -61.375, 'BSPI7032': 64.0, 'BSPI7033': 21.0}} Sending Telemetry Result Status: True
2023-05-13 20:42:11.741 - XcomLAN.thingsboard.thingsboard_client:Studer Xcom-LAN Node:N01 - INFO - Send to Node: N01 Telemetry Content: {'ts': 1684003325951, 'values': {'VT2I11000': 48.75, 'VT2I11004': -0.000278472900390625, 'VT2I11016': 10, 'VT2I11038': 3, 'VT2I11039': 48.71875, 'VT2I11040': -0.0018205642700195312, 'VT2I11041': 60.90625, 'VT2I11043': -8.952617645263672e-05, 'VT2I11045': 29.109375, 'VT2I11061': 0, 'VT2I11062': 0, 'VT2I11082': 0}} Sending Telemetry Result Status: True
2023-05-13 20:42:12.234 - XcomLAN.thingsboard.thingsboard_client:Studer Xcom-LAN Node:N01 - INFO - Send to Node: N01 Telemetry Content: {'ts': 1684003325951, 'values': {'VT1I11000': 48.75, 'VT1I11004': 0.0, 'VT1I11016': 1, 'VT1I11038': 3, 'VT1I11039': 48.75, 'VT1I11040': -0.024932861328125, 'VT1I11041': 67.125, 'VT1I11043': 0.0028228759765625, 'VT1I11045': 27.5625, 'VT1I11061': 0, 'VT1I11062': 0, 'VT1I11082': 0}} Sending Telemetry Result Status: True
2023-05-13 20:42:14.716 - XcomLAN.thingsboard.thingsboard_client:Studer Xcom-LAN Node:N01 - INFO - Send to Node: N01 Telemetry Content: {'ts': 1684003325948, 'values': {'XT1I3010': 4, 'XT1I3020': 0, 'XT1I3021': 231.0, 'XT1I3022': 6.734375, 'XT1I3023': 1.21875, 'XT1I3028': 1, 'XT1I3054': 2, 'XT1I3055': 4, 'XT1I3086': 1, 'XT1I3090': 48.640625, 'XT1I3092': 48.734375, 'XT1I3095': -31.0, 'XT1I3097': 1.7470703125, 'XT1I3098': 1.40625, 'XT1I3101': 1.4052734375, 'XT1I3103': 51.0, 'XT1I3110': 50.0, 'XT1I3113': 243.0, 'XT1I3116': 0.0, 'XT1I3119': 0.0160064697265625, 'XT1I3122': 50.015625, 'XT1I3136': 1.2939453125, 'XT1I3137': 0.0, 'XT1I3138': 0.0, 'XT1I3139': 1.3720703125}} Sending Telemetry Result Status: True
2023-05-13 20:42:14.822 - XcomLAN.thingsboard.thingsboard_client:Studer Xcom-LAN Node:N01 - INFO - Send to Node: N01 Telemetry Content: {'ts': 1684003325949, 'values': {'XT2I3010': 4, 'XT2I3020': 0, 'XT2I3021': 230.0, 'XT2I3022': 6.578125, 'XT2I3023': 1.703125, 'XT2I3028': 1, 'XT2I3054': 2, 'XT2I3055': 4, 'XT2I3086': 1, 'XT2I3090': 48.734375, 'XT2I3092': 48.734375, 'XT2I3095': -34.0, 'XT2I3097': 1.8681640625, 'XT2I3098': 1.5146484375, 'XT2I3101': 1.51953125, 'XT2I3103': 48.0, 'XT2I3110': 50.0, 'XT2I3113': 243.0, 'XT2I3116': 0.0, 'XT2I3119': 0.0160064697265625, 'XT2I3122': 50.015625, 'XT2I3136': 1.3818359375, 'XT2I3137': 0.0, 'XT2I3138': 0.0, 'XT2I3139': 1.3720703125}} Sending Telemetry Result Status: True
starosti-problémy :
V případě napojení na Studer portál je jeho funkčnost ovlivněna
- po instalaci a prvotním spuštění jsem narazil na tuto chybu -
https://github.com/hesso-valais/scom/issues/2 . Mám pocit, že pomohl reinstall, ale POZOR na verzi SCOM, je potřeba instalovat tu správnou - nebyla to ta nejbovější.
- aktuální přehled se nemusí ukázat napoprvé (MOXA holt nestíhá, takže stačí dát refresh a už to jde)
- grafy v záložce Graphs nefungují, protože nedojde ke stažení dat po půlnoci - řešením by bylo přerušit pravidelné vyčítání hodnot mezi 00:00 až 00:30 (to ještě potřebuju vyzkoušet)
- vyčítání dat a posílání do MQTT není najednou, ale postupně dle zařízení a to není úplně ideální
- InfluxDB ukládá data každých 10 sekund, to zatím netuším proč a je tam spousta null řádků - to bude asi nastavením HA+MQTT než tímto skriptem
Pro odchytávání načítaných dat do MQTT se mi osvědčil MQTT Explorer a na základě toho se lépe nastavuje konfigurace MQTT v configuration.yaml.
- Mqtt_explorer.jpg (139.65 KiB) Zobrazeno 4180 x
Data co jdou do MQTT vypadají takto :
Kód: Vybrat vše
{"N01": {"ts": 1684212896654, "values": {"VTALLI11043": 0.33740234375}}}
{"N01": {"ts": 1684212896655, "values": {"BSPI7030": 49.65625, "BSPI7031": 8.0546875, "BSPI7032": 67.0, "BSPI7033": 20.40625}}}
{"N01": {"ts": 1684212896654, "values": {"VT1I11000": 49.84375, "VT1I11004": 0.322265625, "VT1I11016": 8, "VT1I11038": 3, "VT1I11039": 49.84375, "VT1I11040": 6.76953125, "VT1I11041": 68.875, "VT1I11043": 0.33740234375, "VT1I11045": 25.9375, "VT1I11061": 0, "VT1I11062": 0, "VT1I11082": 0}}}
{"N01": {"ts": 1684212896655, "values": {"VT2I11000": 49.8125, "VT2I11004": 0.5625, "VT2I11016": 8, "VT2I11038": 3, "VT2I11039": 49.84375, "VT2I11040": 12.15625, "VT2I11041": 68.0625, "VT2I11043": 0.60595703125, "VT2I11045": 29.859375, "VT2I11061": 0, "VT2I11062": 0, "VT2I11082": 0}}}
{"N01": {"ts": 1684212896652, "values": {"XT1I3010": 4, "XT1I3020": 0, "XT1I3021": 234.0, "XT1I3022": 2.296875, "XT1I3023": 0.5, "XT1I3028": 1, "XT1I3054": 2, "XT1I3055": 4, "XT1I3086": 1, "XT1I3090": 49.765625, "XT1I3092": 49.765625, "XT1I3095": -10.0, "XT1I3097": 0.615234375, "XT1I3098": 0.489013671875, "XT1I3101": 0.47900390625, "XT1I3103": 38.0, "XT1I3110": 50.0, "XT1I3113": 240.0, "XT1I3116": 0.0, "XT1I3119": 0.0160064697265625, "XT1I3122": 50.0, "XT1I3136": 0.465087890625, "XT1I3137": 0.0, "XT1I3138": 0.0, "XT1I3139": 0.4990234375}}}\
{"N01": {"ts": 1684212896653, "values": {"XT2I3010": 4, "XT2I3020": 0, "XT2I3021": 234.0, "XT2I3022": 0.0, "XT2I3023": 0.0, "XT2I3028": 1, "XT2I3054": 2, "XT2I3055": 4, "XT2I3086": 1, "XT2I3090": 49.84375, "XT2I3092": 49.84375, "XT2I3095": 0.0, "XT2I3097": 0.037994384765625, "XT2I3098": 0.0, "XT2I3101": 0.0149993896484375, "XT2I3103": 24.0, "XT2I3110": 50.0, "XT2I3113": 240.0, "XT2I3116": 0.0, "XT2I3119": 0.0160064697265625, "XT2I3122": 50.0, "XT2I3136": 0.0, "XT2I3137": 0.0, "XT2I3138": 0.0, "XT2I3139": 0.0}}}
Moje nastavení v configuration.yaml :
Kód: Vybrat vše
mqtt:
sensor:
- name: "State_of_Charge_Battery"
unique_id: "State_of_Charge_Battery"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "%"
value_template: >
{% if value_json.N01['values'].BSPI7032 is defined %}
{{ value_json.N01['values'].BSPI7032 }}
{% else %}
{{ states('sensor.State_of_Charge_Battery') }}
{% endif %}
- name: "Output_Power_XTM1"
unique_id: "Output_Power_XTM1"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "kWh"
value_template: >
{% if value_json.N01['values'].XT1I3139 is defined %}
{{ value_json.N01['values'].XT1I3139*1000 }}
{% else %}
{{ states('sensor.Output_Power_XTM1') }}
{% endif %}
- name: "Output_Power_XTM2"
unique_id: "Output_Power_XTM2"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "kWh"
value_template: >
{% if value_json.N01['values'].XT2I3139 is defined %}
{{ value_json.N01['values'].XT2I3139*1000 }}
{% else %}
{{ states('sensor.Output_Power_XTM2') }}
{% endif %}
- name: "Power_of_the_PV_VT1"
unique_id: "Power_of_the_PV_VT1"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "kWh"
value_template: >
{% if value_json.N01['values'].VT1I11004 is defined %}
{{ value_json.N01['values'].VT1I11004*1000 }}
{% else %}
{{ states('sensor.Power_of_the_PV_VT1') }}
{% endif %}
- name: "Power_of_the_PV_VT2"
unique_id: "Power_of_the_PV_VT2"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "kWh"
value_template: >
{% if value_json.N01['values'].VT2I11004 is defined %}
{{ value_json.N01['values'].VT2I11004*1000 }}
{% else %}
{{ states('sensor.Power_of_the_PV_VT2') }}
{% endif %}
- name: "Battery_current_min_avg"
unique_id: "Battery_current_min_avg"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "A"
value_template: >
{% if value_json.N01['values'].BSPI7031 is defined %}
{{ value_json.N01['values'].BSPI7031 }}
{% else %}
{{ states('sensor.Battery_current_min_avg') }}
{% endif %}
- name: "Battery_voltage_min_avg"
unique_id: "Battery_voltage_min_avg"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "V"
value_template: >
{% if value_json.N01['values'].BSPI7030 is defined %}
{{ value_json.N01['values'].BSPI7030 }}
{% else %}
{{ states('sensor.Battery_voltage_min_avg') }}
{% endif %}
- name: "Battery_power"
unique_id: "Battery_power"
state_class: measurement
state_topic: "v1/gateway/telemetry"
unit_of_measurement: "W"
value_template: >
{% if value_json.N01['values'].BSPI7030 is defined %}
{{ (value_json.N01['values'].BSPI7031)|float(0) * (value_json.N01['values'].BSPI7030)|float(0) }}
{% else %}
{{ states('sensor.Battery_power') }}
{% endif %}
Snad je to aspoň trochu pochopitelné, bohužel nejsem spisovatel a jako každý ajťák dokumentace nejsou mojí silnou stránkou. Určitě se vývoji budu dál věnovat, hlavně zobrazení v Grafana, kde mě čeká spousta práce, jen toho času tolik není.
Snad jsem na nic nezapomněl.
Pokud někdo přijde na vylepšení skriptu nebo poskytne kód pro Grafanu, určitě to mnoha lidem pomůže.