Compare commits
2 commits
master
...
franga2000
Author | SHA1 | Date | |
---|---|---|---|
305e19b3b8 | |||
![]() |
e57f803a52 |
32 changed files with 571 additions and 90 deletions
6
ansible_deploy/barcoinv.yml
Normal file
6
ansible_deploy/barcoinv.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
all:
|
||||||
|
hosts:
|
||||||
|
mmctrl-p1:
|
||||||
|
projectors:
|
||||||
|
- projector-p1-center
|
||||||
|
- projector-p1-side
|
30
ansible_deploy/conf.j2
Normal file
30
ansible_deploy/conf.j2
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Hostname: {{ ansible_facts['hostname'] }} running {{ansible_facts['distribution'] }} on address {{ addr }}
|
||||||
|
Connected projectors: {{ barco_G62[0].ip }}
|
||||||
|
|
||||||
|
[global]
|
||||||
|
room = {{ room }}
|
||||||
|
mqttIp = {{ mqtt_ip }}
|
||||||
|
mqttPort = {{ mqtt_port }}
|
||||||
|
|
||||||
|
{% if barco_G62 %}
|
||||||
|
{%+ for projector in barco_G62 +%}
|
||||||
|
[{{projector.model}}.{{projector.position}}]
|
||||||
|
ip = {{projector.ip}}
|
||||||
|
port = {{projector.port}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{%+ if tse_box +%}
|
||||||
|
serial_device = {{ tse_box.serial_device }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{%+ if projector_motors +%} {# change to appropriate thingy for running them #}
|
||||||
|
{% end if %}
|
||||||
|
|
||||||
|
{%+ if extron_audio +%}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{%+ if extron_video +%}
|
||||||
|
|
||||||
|
{% endif %}
|
4
ansible_deploy/inventory.ini
Normal file
4
ansible_deploy/inventory.ini
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[prHosts]
|
||||||
|
192.168.122.245
|
||||||
|
|
||||||
|
|
23
ansible_deploy/inventory.yml
Normal file
23
ansible_deploy/inventory.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
P01:
|
||||||
|
hosts:
|
||||||
|
192.168.122.245:
|
||||||
|
vars:
|
||||||
|
room: P01
|
||||||
|
mqtt_ip: localhost
|
||||||
|
mqtt_port: 1883
|
||||||
|
|
||||||
|
barco_G62:
|
||||||
|
- position: main
|
||||||
|
model: barco_G62
|
||||||
|
port: 3023
|
||||||
|
ip: 192.168.192.13
|
||||||
|
- position : side
|
||||||
|
model: barco_G62
|
||||||
|
port: 3023
|
||||||
|
ip: 192.168.192.14
|
||||||
|
|
||||||
|
barco_old:
|
||||||
|
main_ip: 1.1.1.1
|
||||||
|
|
||||||
|
P22:
|
||||||
|
|
152
ansible_deploy/playbook.yaml
Normal file
152
ansible_deploy/playbook.yaml
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
- name: test playbook
|
||||||
|
hosts: P01
|
||||||
|
|
||||||
|
#vars_files:
|
||||||
|
# - secret
|
||||||
|
vars:
|
||||||
|
addr: "192.168.122.245"
|
||||||
|
os_environment:
|
||||||
|
- key: VITE_MQTT_HOST
|
||||||
|
value: polztest.local
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: ping hosts
|
||||||
|
ansible.builtin.ping:
|
||||||
|
|
||||||
|
- name: install pkgs
|
||||||
|
become: true
|
||||||
|
#become_user: root
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- mosquitto
|
||||||
|
- python3-poetry
|
||||||
|
#- npm
|
||||||
|
- nginx
|
||||||
|
state: latest
|
||||||
|
|
||||||
|
#- name: set env
|
||||||
|
|
||||||
|
- name: ensures services dir exists
|
||||||
|
file:
|
||||||
|
path: "/home/kat/pyServices"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: ensures services dir exists
|
||||||
|
file:
|
||||||
|
path: "/home/kat/pyServices/fri-mm-maline"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Copy poetry
|
||||||
|
become: true
|
||||||
|
ansible.builtin.copy:
|
||||||
|
#seuser: root
|
||||||
|
src: "../{{ item }}"
|
||||||
|
dest: "/home/kat/pyServices/fri-mm-maline/{{ item }}"
|
||||||
|
owner: kat
|
||||||
|
group: kat
|
||||||
|
mode: '0644'
|
||||||
|
backup: yes
|
||||||
|
loop:
|
||||||
|
- poetry.lock
|
||||||
|
- pyproject.toml
|
||||||
|
- README.md
|
||||||
|
|
||||||
|
- name: copy python scripts
|
||||||
|
become: true
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "../{{ item }}"
|
||||||
|
dest: "/home/kat/pyServices/fri-mm-maline"
|
||||||
|
owner: kat
|
||||||
|
group: kat
|
||||||
|
mode: '0644'
|
||||||
|
backup: yes
|
||||||
|
loop:
|
||||||
|
- barco_telnet/barco_G62_control.py
|
||||||
|
- extron_audio_matrix/extron_audio_matrix_telnet_control.py
|
||||||
|
- extron_audio_matrix/extron_audio_matrix_telnet_interpreter.py
|
||||||
|
- projector_motors/projector_motors.py
|
||||||
|
- tse_serial/tse_serial_controler.py
|
||||||
|
- tse_serial/tse_serial_interpreter.py
|
||||||
|
- config.toml #TODO GENERATE CONFIG
|
||||||
|
|
||||||
|
|
||||||
|
- name: poetry installation from thing
|
||||||
|
ansible.builtin.shell:
|
||||||
|
cmd: "poetry install"
|
||||||
|
chdir: "/home/kat/pyServices/fri-mm-maline"
|
||||||
|
|
||||||
|
- name: Copy barco config
|
||||||
|
become: true
|
||||||
|
ansible.builtin.copy:
|
||||||
|
#seuser: root
|
||||||
|
src: "../{{ item }}"
|
||||||
|
dest: /lib/systemd/system
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0644'
|
||||||
|
#backup: yes
|
||||||
|
loop:
|
||||||
|
- barco@.service
|
||||||
|
- extron_audio.service
|
||||||
|
- mqtt_init.service
|
||||||
|
- projector_motors.service
|
||||||
|
- tse_box.service
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- name: Copy mosqitconfig
|
||||||
|
become: true
|
||||||
|
ansible.builtin.copy:
|
||||||
|
#seuser: root
|
||||||
|
src: ../mosquitto.conf
|
||||||
|
dest: /etc/mosquitto/conf.d
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0644'
|
||||||
|
backup: yes
|
||||||
|
|
||||||
|
# - name: set env
|
||||||
|
# become: true
|
||||||
|
# ansible.builtin.lineinfile:
|
||||||
|
# dest: "/etc/environment"
|
||||||
|
# state: present
|
||||||
|
# regexp: "^{{ item.key }}="
|
||||||
|
# line: "{{ item.key }}={{ item.value }}"
|
||||||
|
# with_items: "{{ os_environment }}"
|
||||||
|
|
||||||
|
- name: copy frontend to webroot
|
||||||
|
become: true
|
||||||
|
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: /home/kat/Documents/polzp/fri_multimedia_rework/frontend/vju_display/dist
|
||||||
|
#src: /home/kat/fri_multimedia_rework/frontend/vju_display/dist
|
||||||
|
#remote_src: true
|
||||||
|
dest: /srv/www
|
||||||
|
#dest: /home/kat/testoa
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0755'
|
||||||
|
backup: yes
|
||||||
|
|
||||||
|
- name: enable modules
|
||||||
|
become: true
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
#name: "{{[ 'barco@main.service', 'barco@side.service' ]}}"
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: stopped
|
||||||
|
enabled: false
|
||||||
|
loop:
|
||||||
|
- barco@main.service
|
||||||
|
- barco@side.service
|
||||||
|
- extron_audio.service
|
||||||
|
- mqtt_init.service
|
||||||
|
- projector_motors.service
|
||||||
|
- tse_box.service
|
||||||
|
|
||||||
|
|
||||||
|
- name: mosquitto service and reload
|
||||||
|
become: true
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: mosquitto.service
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
15
ansible_deploy/service.j2
Normal file
15
ansible_deploy/service.j2
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[Unit]
|
||||||
|
Description={{ script_file }}
|
||||||
|
After=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/poetry run /usr/bin/python3 {{ script_file }}
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
User=kat
|
||||||
|
Group=kat
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
DefaultInstance=main
|
37
ansible_deploy/testbook.yaml
Normal file
37
ansible_deploy/testbook.yaml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
- name: test playbook
|
||||||
|
hosts: P01
|
||||||
|
|
||||||
|
#vars_files:
|
||||||
|
# - secret
|
||||||
|
vars:
|
||||||
|
addr: "192.168.122.245"
|
||||||
|
os_environment:
|
||||||
|
- key: VITE_MQTT_HOST
|
||||||
|
value: polztest.local
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: test things
|
||||||
|
when: barco_g62 is defined
|
||||||
|
block:
|
||||||
|
#- name: ping hosts
|
||||||
|
# ansible.builtin.ping:
|
||||||
|
|
||||||
|
- name: template config.toml
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: ./conf.j2
|
||||||
|
dest: /home/kat/testo/conf.txt
|
||||||
|
- name: template barco systemd service
|
||||||
|
vars:
|
||||||
|
script_file: "/home/kat/pyServices/fri-mm-maline/barco_G62_control.py %i"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: ./service.j2
|
||||||
|
dest: /home/kat/testo/barc.serv
|
||||||
|
- name: enable barcos
|
||||||
|
ansible.builtin.ping:
|
||||||
|
#itd itd itd
|
||||||
|
- name: test second
|
||||||
|
when: barco_old is defined
|
||||||
|
block:
|
||||||
|
- name: pingerino
|
||||||
|
ansible.builtin.ping:
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Barco projector control
|
Description=Barco projector control
|
||||||
After=mqtt_init.service
|
After=multi-user.target
|
||||||
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/bin/python3 /home/rpi/barco_telnet_control.py %name
|
ExecStart=/usr/bin/poetry run python3 /home/kat/pyServices/fri-mm-maline/barco_G62_control.py %i
|
||||||
User=rpi
|
|
||||||
Group=rpi
|
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
|
WorkingDirectory=/home/kat/pyServices/fri-mm-maline
|
||||||
|
User=kat
|
||||||
|
Group=kat
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=mqtt_init.service
|
WantedBy=multi-user.target
|
||||||
|
DefaultInstance=main
|
|
@ -3,14 +3,16 @@ import socket
|
||||||
import aiomqtt
|
import aiomqtt
|
||||||
import telnetlib3
|
import telnetlib3
|
||||||
import toml
|
import toml
|
||||||
from socket import gethostname
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
mainBarcoIP = '192.168.192.12'
|
#GLOBALS
|
||||||
sideBarcoIP = '192.168.192.13'
|
|
||||||
|
|
||||||
# TODO all the checks and stuff
|
room = "undefined"
|
||||||
# TODO MAKE THIS CONFIGURALBE
|
position = "undefined"
|
||||||
|
barcoIP = "undefined"
|
||||||
|
telnetPort = 3023
|
||||||
|
mqttPort = 1883
|
||||||
|
mqttIP = 'localhost'
|
||||||
|
|
||||||
cmdMap = {
|
cmdMap = {
|
||||||
'power': 'POWR',
|
'power': 'POWR',
|
||||||
|
@ -31,11 +33,13 @@ def parse_barco_response(raw: str):
|
||||||
status = '1' if status == '01' else '0'
|
status = '1' if status == '01' else '0'
|
||||||
return cmd, status
|
return cmd, status
|
||||||
|
|
||||||
|
#TODO MAKE THESE USE GLOBAL INSTEAD OF PASSING BARCO POSITION IN MAIN
|
||||||
|
|
||||||
async def barco_telnet_command(client, writer, select: str):
|
async def barco_telnet_command(client, writer, select: str):
|
||||||
onSub = f"p01/projectors/{select}/#"
|
global room
|
||||||
|
onSub = f"{room}/projectors/{select}/#"
|
||||||
#print('TEST', onSub)
|
#print('TEST', onSub)
|
||||||
onMatch = f"p01/projectors/{select}/command/+"
|
onMatch = f"{room}/projectors/{select}/command/+"
|
||||||
await client.subscribe(onSub)
|
await client.subscribe(onSub)
|
||||||
#async with client.messages as msgs:
|
#async with client.messages as msgs:
|
||||||
async for mesg in client.messages:
|
async for mesg in client.messages:
|
||||||
|
@ -55,6 +59,7 @@ async def barco_telnet_command(client, writer, select: str):
|
||||||
|
|
||||||
|
|
||||||
async def barco_telnet_read_status(client, reader, select: str):
|
async def barco_telnet_read_status(client, reader, select: str):
|
||||||
|
global room
|
||||||
while True:
|
while True:
|
||||||
output = await reader.readuntil(b']')
|
output = await reader.readuntil(b']')
|
||||||
raw_response: str = output.decode().strip() # strip not necessary? needed for local netcat testing though
|
raw_response: str = output.decode().strip() # strip not necessary? needed for local netcat testing though
|
||||||
|
@ -63,7 +68,7 @@ async def barco_telnet_read_status(client, reader, select: str):
|
||||||
if parsed == None:
|
if parsed == None:
|
||||||
continue #TODO alert for errors
|
continue #TODO alert for errors
|
||||||
print(f"Updating topic [{parsed[0]}] with value [{parsed[1]}]")
|
print(f"Updating topic [{parsed[0]}] with value [{parsed[1]}]")
|
||||||
await client.publish(f"p01/projectors/{select}/status/{parsed[0]}", payload=parsed[1])
|
await client.publish(f"{room}/projectors/{select}/status/{parsed[0]}", payload=parsed[1])
|
||||||
|
|
||||||
|
|
||||||
async def barco_telnet_query_status(writer, select: str):
|
async def barco_telnet_query_status(writer, select: str):
|
||||||
|
@ -84,23 +89,25 @@ async def barco_telnet_query_status(writer, select: str):
|
||||||
# await asyncio.gather(task_status_query, task_status_reader, task_control)
|
# await asyncio.gather(task_status_query, task_status_reader, task_control)
|
||||||
|
|
||||||
|
|
||||||
async def main(mqttIP=localhost, barcoIP=localhost, mqttPort=1883, telnetPort=3023):
|
async def main():
|
||||||
conf = toml.load('config.toml')
|
global barcoPosition, barcoIP, telnetPort, mqttIP, mqttPort
|
||||||
barco
|
if len(sys.argv) < 2:
|
||||||
# mainBarcoIP = conf[gethostname()]['projektor_glavni']
|
sys.exit("No position provided")
|
||||||
# sideBarcoIP = conf[gethostname()]['projektor_stranski']
|
else:
|
||||||
mainReader, mainWriter = await telnetlib3.open_connection(barcoIP, telnetPort)
|
barcoPosition = sys.argv[1]
|
||||||
#sideReader, sideWriter = await telnetlib3.open_connection('localhost', 3024)
|
|
||||||
async with aiomqtt.Client(mqttIP, mqttPort) as client:
|
|
||||||
task_status_query_main = asyncio.create_task(barco_telnet_query_status(mainWriter, 'glavni'))
|
|
||||||
task_status_reader_main = asyncio.create_task(barco_telnet_read_status(client, mainReader, 'glavni'))
|
|
||||||
task_control_main = asyncio.create_task(barco_telnet_command(client, mainWriter, 'glavni'))
|
|
||||||
#task_status_query_side = asyncio.create_task(barco_telnet_query_status(sideWriter, 'stranski'))
|
|
||||||
#task_status_reader_side = asyncio.create_task(barco_telnet_read_status(client, sideReader, 'stranski'))
|
|
||||||
#task_control_side = asyncio.create_task(barco_telnet_command(client, sideWriter, 'stranski'))
|
|
||||||
|
|
||||||
await asyncio.gather(task_status_query_main, task_status_reader_main, task_control_main)
|
conf = toml.load('./config.toml')
|
||||||
#task_status_query_side, task_status_reader_side, task_control_side)
|
g62Barcos = {k: v for k,v in barcos["barco_G62"].items()}
|
||||||
|
currentBarco = newBarcos[barcoPosition]
|
||||||
|
barcoIP = currentBarco['ip']
|
||||||
|
|
||||||
|
barcoReader, barcoWriter = await telnetlib3.open_connection(barcoIP, telnetPort)
|
||||||
|
async with aiomqtt.Client(mqttIP, mqttPort) as client:
|
||||||
|
task_status_query_barco = asyncio.create_task(barco_telnet_query_status(barcoWriter, barcoPosition))
|
||||||
|
task_status_reader_barco = asyncio.create_task(barco_telnet_read_status(client, barcoReader, barcoPosition))
|
||||||
|
task_control_barco = asyncio.create_task(barco_telnet_command(client, barcoWriter, barcoPosition))
|
||||||
|
|
||||||
|
await asyncio.gather(task_status_query_barco, task_status_reader_barco, task_control_barco)
|
||||||
|
|
||||||
|
|
||||||
### fuj to, ne tk delat
|
### fuj to, ne tk delat
|
17
config.toml
17
config.toml
|
@ -1,5 +1,14 @@
|
||||||
[p1malina]
|
[globals]
|
||||||
projektor_glavni = ""
|
room = 'P01'
|
||||||
projektor_stranski = ""
|
mqttIp = 'localhost'
|
||||||
extron_audio_matrika = ""
|
mqttPort = '1883'
|
||||||
|
|
||||||
|
|
||||||
|
[barco.novi.glavni]
|
||||||
|
ip = '192.168.192.12'
|
||||||
|
port = '3023'
|
||||||
|
|
||||||
|
[barco.novi.stranski]
|
||||||
|
ip = '192.168.192.16' # or smth
|
||||||
|
port = '3023'
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Extron audio matrix control
|
Description=Extron audio matrix control
|
||||||
After=mqtt_init.service
|
After=multi-user.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/bin/python3 /home/rpi/extron_audio_matrix_telnet_control.py
|
ExecStart=/usr/bin/python3 /home/rpi/extron_audio_matrix_telnet_control.py
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
|
User=kat
|
||||||
|
Group=kat
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=mqtt_init.service
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
VITE_MQTT_HOST=localhost #p01malina.local
|
VITE_MQTT_HOST=192.168.122.245
|
||||||
VITE_MQTT_PORT=8080
|
VITE_MQTT_PORT=8080
|
||||||
VITE_MQTT_SSL=false
|
VITE_MQTT_SSL=false
|
2
frontend/vju_display/.gitignore
vendored
2
frontend/vju_display/.gitignore
vendored
|
@ -28,3 +28,5 @@ coverage
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
.vscode/
|
24
frontend/vju_display/package-lock.json
generated
24
frontend/vju_display/package-lock.json
generated
|
@ -8,17 +8,19 @@
|
||||||
"name": "vju_display",
|
"name": "vju_display",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"paho-mqtt": "^1.1.0",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.12",
|
||||||
"vue-paho-mqtt": "^0.6.5"
|
"vue-paho-mqtt": "^0.6.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/node20": "^20.1.4",
|
"@tsconfig/node20": "^20.1.4",
|
||||||
"@types/node": "^20.17.0",
|
"@types/node": "^20.17.16",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
"@vue/tsconfig": "^0.5.1",
|
"@vue/tsconfig": "^0.5.1",
|
||||||
"npm-run-all2": "^7.0.1",
|
"npm-run-all2": "^7.0.1",
|
||||||
"typescript": "~5.6.0",
|
"typescript": "~5.6.0",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.14",
|
||||||
"vue-tsc": "^2.1.6"
|
"vue-tsc": "^2.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -704,9 +706,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.17.0",
|
"version": "20.17.16",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz",
|
||||||
"integrity": "sha512-a7zRo0f0eLo9K5X9Wp5cAqTUNGzuFLDG2R7C4HY2BhcMAsxgSPuRvAC1ZB6QkuUQXf0YZAgfOX2ZyrBa2n4nHQ==",
|
"integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1044,6 +1046,12 @@
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fs": {
|
||||||
|
"version": "0.0.1-security",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
|
||||||
|
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
@ -1392,9 +1400,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.4.10",
|
"version": "5.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz",
|
||||||
"integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
|
"integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -11,17 +11,19 @@
|
||||||
"type-check": "vue-tsc --build --force"
|
"type-check": "vue-tsc --build --force"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"paho-mqtt": "^1.1.0",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.12",
|
||||||
"vue-paho-mqtt": "^0.6.5"
|
"vue-paho-mqtt": "^0.6.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/node20": "^20.1.4",
|
"@tsconfig/node20": "^20.1.4",
|
||||||
"@types/node": "^20.17.0",
|
"@types/node": "^20.17.16",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
"@vue/tsconfig": "^0.5.1",
|
"@vue/tsconfig": "^0.5.1",
|
||||||
"npm-run-all2": "^7.0.1",
|
"npm-run-all2": "^7.0.1",
|
||||||
"typescript": "~5.6.0",
|
"typescript": "~5.6.0",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.14",
|
||||||
"vue-tsc": "^2.1.6"
|
"vue-tsc": "^2.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import { createPahoMqttPlugin } from 'vue-paho-mqtt'
|
import { createPahoMqttPlugin } from 'vue-paho-mqtt'
|
||||||
|
|
||||||
|
let urlParams = new URLSearchParams(window.location.search);
|
||||||
createApp(App)
|
createApp(App)
|
||||||
.use(
|
.use(
|
||||||
createPahoMqttPlugin({
|
createPahoMqttPlugin({
|
||||||
|
@ -13,9 +14,13 @@ createApp(App)
|
||||||
},
|
},
|
||||||
|
|
||||||
MqttOptions: {
|
MqttOptions: {
|
||||||
host: import.meta.env.VITE_MQTT_HOST,
|
//host: import.meta.env.VITE_MQTT_HOST,
|
||||||
port: parseInt(import.meta.env.VITE_MQTT_PORT),
|
//host: "localhost",
|
||||||
useSSL: ["1", "true", "True"].includes(import.meta.env.VITE_MQTT_SSL),
|
host: urlParams.get('mqtt') || 'localhost',
|
||||||
|
port: 8080,
|
||||||
|
useSSL: false,
|
||||||
|
//port: parseInt(import.meta.env.VITE_MQTT_PORT),
|
||||||
|
//useSSL: ["1", "true", "True"].includes(import.meta.env.VITE_MQTT_SSL),
|
||||||
clientId: `vju-${Math.random() * 9999}`,
|
clientId: `vju-${Math.random() * 9999}`,
|
||||||
//mainTopic: '',
|
//mainTopic: '',
|
||||||
enableMainTopic: false
|
enableMainTopic: false
|
||||||
|
|
|
@ -8,9 +8,15 @@ export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
],
|
],
|
||||||
|
//base: './',
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
hmr: {
|
||||||
|
host: 'localhost'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -231,10 +231,10 @@
|
||||||
# listener 0 /tmp/mosquitto.sock
|
# listener 0 /tmp/mosquitto.sock
|
||||||
#
|
#
|
||||||
# listener port-number [ip address/host name/unix socket path]
|
# listener port-number [ip address/host name/unix socket path]
|
||||||
listener 1883
|
listener 1883 0.0.0.0
|
||||||
listener 8080
|
listener 8080
|
||||||
protocol websockets
|
protocol websockets
|
||||||
protocol mqtt
|
#protocol mqtt
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
# By default, a listener will attempt to listen on all supported IP protocol
|
# By default, a listener will attempt to listen on all supported IP protocol
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Control for projector motors
|
Description=Control for projector motors
|
||||||
After=mqtt_init.service
|
After=multi-user.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/bin/python3 /home/rpi/projector_motors.py
|
ExecStart=/usr/bin/python3 /home/rpi/projector_motors.py
|
||||||
Restart=always
|
Restart=always
|
||||||
Type=simple
|
Type=simple
|
||||||
|
User=kat
|
||||||
|
Group=kat
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=mqtt_init.service
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ description = ""
|
||||||
authors = ["Your Name <you@example.com>"]
|
authors = ["Your Name <you@example.com>"]
|
||||||
license = "GPLv3"
|
license = "GPLv3"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
package-mode = false
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.10"
|
python = "^3.10"
|
||||||
|
|
19
tomlTest.py
Normal file
19
tomlTest.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import toml
|
||||||
|
|
||||||
|
tomlConf = toml.load('./config.toml')
|
||||||
|
|
||||||
|
print(tomlConf)
|
||||||
|
barcos = {k: v for k, v in tomlConf["barco"].items()}
|
||||||
|
print()
|
||||||
|
print(barcos)
|
||||||
|
|
||||||
|
newBarcos = {k: v for k,v in barcos["novi"].items()}
|
||||||
|
|
||||||
|
print(newBarcos)
|
||||||
|
|
||||||
|
for pos in newBarcos.keys():
|
||||||
|
atrs = newBarcos[pos]
|
||||||
|
print(atrs)
|
||||||
|
ip = atrs['ip']
|
||||||
|
# etc
|
||||||
|
# launch scripty thingy with atrs[IP], itd
|
18
touch_display/inventory.yml
Normal file
18
touch_display/inventory.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
predavalnice_pi:
|
||||||
|
# Skupne spremenljivke za vse zaslone
|
||||||
|
vars:
|
||||||
|
# Check here: https://github.com/leukipp/touchkio/releases
|
||||||
|
touchkio_version: "1.1.2"
|
||||||
|
# Multimedia network
|
||||||
|
static_ip_netmask: "255.255.255.0"
|
||||||
|
static_ip_gateway: "192.168.190.1"
|
||||||
|
static_ip_dns_servers:
|
||||||
|
- "212.235.188.28"
|
||||||
|
- "212.235.188.29"
|
||||||
|
hosts:
|
||||||
|
p01_touch_display:
|
||||||
|
predavalnica: p01
|
||||||
|
static_ip_address: "192.168.190.111"
|
||||||
|
hostname: "p01_touch_display.local"
|
||||||
|
kiosk_url: "http://192.168.190.110"
|
||||||
|
|
6
touch_display/main.yml
Normal file
6
touch_display/main.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
- hosts: predavalnice_pi
|
||||||
|
# TODO: better include (import playbook)
|
||||||
|
tasks:
|
||||||
|
- include_tasks: static_ip.yml
|
||||||
|
- include_tasks: pi_stuff.yml
|
||||||
|
- include_tasks: touch_display.yml
|
36
touch_display/pi_stuff.yml
Normal file
36
touch_display/pi_stuff.yml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
- name: Enable boot splash screen
|
||||||
|
become: true
|
||||||
|
shell: "raspi-config nonint get_boot_splash && raspi-config nonint do_boot_splash 0"
|
||||||
|
register: boot_splash
|
||||||
|
changed_when:
|
||||||
|
- boot_splash == "1"
|
||||||
|
|
||||||
|
- name: Disable color splash
|
||||||
|
become: true
|
||||||
|
community.general.ini_file:
|
||||||
|
path: /boot/firmware/config.txt
|
||||||
|
option: disable_splash
|
||||||
|
value: 1
|
||||||
|
no_extra_spaces: true
|
||||||
|
|
||||||
|
- name: Change splash image
|
||||||
|
become: true
|
||||||
|
copy:
|
||||||
|
src: splash.png
|
||||||
|
dest: /usr/share/plymouth/themes/pix/splash.png
|
||||||
|
|
||||||
|
- name: Remove desktop bloat
|
||||||
|
become: true
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- gvfs
|
||||||
|
- gnome-keyring
|
||||||
|
- cups
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Switch to wayfire
|
||||||
|
become: true
|
||||||
|
shell: "raspi-config nonint is_wayfire && raspi-config nonint do_wayland W2"
|
||||||
|
register: result
|
||||||
|
failed_when: ( result.rc not in [ 0, 1 ] )
|
||||||
|
changed_when: ( result.rc == 1 )
|
BIN
touch_display/splash.png
Normal file
BIN
touch_display/splash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
28
touch_display/static_ip.yml
Normal file
28
touch_display/static_ip.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
- name: Configure static IP address (using Network Manager).
|
||||||
|
become: yes
|
||||||
|
community.general.nmcli:
|
||||||
|
conn_name: "Multimedia net"
|
||||||
|
ifname: eth0
|
||||||
|
type: ethernet
|
||||||
|
ip4: "{{ static_ip_address }}/{{ static_ip_netmask | ansible.utils.ipaddr('prefix') }}"
|
||||||
|
gw4: "{{static_ip_gateway}}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Restart NetworkManager
|
||||||
|
become: yes
|
||||||
|
service:
|
||||||
|
name: NetworkManager
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: Wait for network to be available
|
||||||
|
become: yes
|
||||||
|
wait_for_connection:
|
||||||
|
timeout: 60
|
||||||
|
|
||||||
|
# TODO: get rid of netaddr
|
||||||
|
- name: Display new IP configuration
|
||||||
|
debug:
|
||||||
|
msg: "Static IP configured: {{ static_ip_address }}/{{ static_ip_netmask | ansible.utils.ipaddr('prefix') }}"
|
||||||
|
|
||||||
|
|
32
touch_display/touch_display.yml
Normal file
32
touch_display/touch_display.yml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
- name: Download .deb file
|
||||||
|
get_url:
|
||||||
|
url: "https://github.com/leukipp/touchkio/releases/download/v{{ touchkio_version }}/touchkio_{{ touchkio_version }}_arm64.deb"
|
||||||
|
dest: "/tmp/touchkio_{{ touchkio_version }}_arm64.deb"
|
||||||
|
register: deb_download
|
||||||
|
|
||||||
|
- name: Install the latest .deb package
|
||||||
|
become: yes
|
||||||
|
apt:
|
||||||
|
deb: "/tmp/touchkio_{{ touchkio_version }}_arm64.deb"
|
||||||
|
when: deb_download is succeeded
|
||||||
|
|
||||||
|
- name: Create systemd user service directory
|
||||||
|
file:
|
||||||
|
path: "{{ ansible_env.HOME }}/.config/systemd/user"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Create systemd user service
|
||||||
|
template:
|
||||||
|
src: touchkio.service.j2
|
||||||
|
dest: "{{ ansible_env.HOME }}/.config/systemd/user/touchkio.service"
|
||||||
|
|
||||||
|
- name: Enable systemd service
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: touchkio
|
||||||
|
enabled: true
|
||||||
|
state: restarted
|
||||||
|
scope: user
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
when: ansible_check_mode == false
|
11
touch_display/touchkio.service.j2
Normal file
11
touch_display/touchkio.service.j2
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[Unit]
|
||||||
|
Description=TouchKio
|
||||||
|
After=graphical.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/touchkio --web-url="{{ kiosk_url }}"
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
|
@ -1,15 +1,15 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=TSE serial control
|
Description=TSE serial control
|
||||||
After=mqtt_init.service
|
After=multi-user.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/bin/python3 /home/rpi/tse_serial_controler.py /home/rpi/tse_serial_interpreter.py
|
ExecStart=/usr/bin/python3 /home/rpi/tse_serial_controler.py /home/rpi/tse_serial_interpreter.py
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
User=rpi
|
User=kat
|
||||||
Group=rpi
|
Group=kat
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=mqtt_init.service
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,26 +4,42 @@ import aioserial
|
||||||
import aiomqtt
|
import aiomqtt
|
||||||
from tse_serial_interpreter import *
|
from tse_serial_interpreter import *
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import toml
|
||||||
|
import serial.tools.list_ports
|
||||||
|
|
||||||
room = 'p01' #TODO make be do get fronm file of configuration
|
room = 'p01' #TODO make be do get fronm file of configuration
|
||||||
|
|
||||||
|
# serPath = '/dev/serial/by-id/'
|
||||||
|
# devLst = os.listdir(serPath)
|
||||||
|
# if len(devLst) < 1:
|
||||||
|
# sys.exit("No serial device found.")
|
||||||
|
# serDev = devLst[0]
|
||||||
|
|
||||||
|
portList = serial.tools.list_ports.comports()
|
||||||
|
if portList < 1:
|
||||||
|
sys.exit("No serial port found")
|
||||||
|
#TODO if multiple ports idk, shouldn't ever happen but still
|
||||||
|
(serport, serdesc, serhwid) = portList[0]
|
||||||
|
|
||||||
|
|
||||||
aser: aioserial.AioSerial = aioserial.AioSerial(
|
aser: aioserial.AioSerial = aioserial.AioSerial(
|
||||||
port='/dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0',
|
port=serport,
|
||||||
#id say not hardcode it ampak kinda nimamo izbire
|
|
||||||
baudrate=1200,
|
baudrate=1200,
|
||||||
parity=serial.PARITY_NONE,
|
parity=serial.PARITY_NONE,
|
||||||
bytesize=serial.EIGHTBITS,
|
bytesize=serial.EIGHTBITS,
|
||||||
stopbits=serial.STOPBITS_ONE
|
stopbits=serial.STOPBITS_ONE
|
||||||
)
|
)
|
||||||
|
|
||||||
#TODO get this from file da ni hardcoded
|
#TODO get this from file da ni hardcoded?
|
||||||
|
# altho itak je ta script za tist specific tse box in so vsi isti
|
||||||
mapping_toggles = {
|
mapping_toggles = {
|
||||||
"master": 1,
|
"master": 1,
|
||||||
"audio": 2,
|
"audio": 2,
|
||||||
"projectors": 3,
|
"projectors": 3,
|
||||||
}
|
}
|
||||||
|
# 4 is not connected to anything
|
||||||
platno_mapping = {
|
platno_mapping = {
|
||||||
"glavni": {
|
"glavni": {
|
||||||
"dol": 5,
|
"dol": 5,
|
||||||
|
@ -40,26 +56,34 @@ shades_mapping = {
|
||||||
"gor": 10
|
"gor": 10
|
||||||
}
|
}
|
||||||
|
|
||||||
#reverse_lookup = {v: k for k, v in mapping.items()} #fujto
|
reverse_lookup = {
|
||||||
|
1: "master",
|
||||||
|
2: "audio",
|
||||||
|
3: "projectors",
|
||||||
|
|
||||||
|
5: "glavni_dol",
|
||||||
|
6: "glavni_gor",
|
||||||
|
|
||||||
|
7: "glavni_dol",
|
||||||
|
8: "glavni_gor",
|
||||||
|
|
||||||
|
9: "shades_dol",
|
||||||
|
10: "shades_gor"
|
||||||
|
}
|
||||||
|
|
||||||
# TODO simple reverse lookup za ko kripa pove kaj
|
|
||||||
# in vse tole
|
|
||||||
|
|
||||||
async def task_status2mqtt(statusClient: aiomqtt.Client):
|
async def task_status2mqtt(statusClient: aiomqtt.Client):
|
||||||
while True:
|
while True:
|
||||||
#data = await aser.read_until_async()
|
data = await aser.read_until_async()
|
||||||
#data = data.decode(errors='ignore').strip()
|
data = data.decode(errors='ignore').strip()
|
||||||
#print("TSE box sent: " + data)
|
print("TSE box sent: " + data)
|
||||||
#relState = resp_to_relay_state(data)
|
relState = resp_to_relay_state(data)
|
||||||
#command = reverse_lookup[relState.relay_id]
|
if relState.relay_id == None:
|
||||||
#action = relState.state
|
continue # TODO handling
|
||||||
#TODO havent figured out a clean way to
|
command = reverse_lookup[relState.relay_id]
|
||||||
# get out the action from topic yet as they are
|
|
||||||
# not always on the same level
|
action = relState.state
|
||||||
# REWORK
|
|
||||||
# probably just do it the most straight forward way
|
|
||||||
# with some more code
|
|
||||||
#publishTopic = f"{room}/"
|
#publishTopic = f"{room}/"
|
||||||
#publishPayload = "ON" if relState.state else "OFF"
|
#publishPayload = "ON" if relState.state else "OFF"
|
||||||
#print("Publishing [" + publishPayload + "] to topic [" + publishTopic + "]")
|
#print("Publishing [" + publishPayload + "] to topic [" + publishTopic + "]")
|
||||||
|
|
|
@ -8,17 +8,15 @@ class RelayState:
|
||||||
|
|
||||||
|
|
||||||
def resp_to_relay_state(cmd: str) -> RelayState:
|
def resp_to_relay_state(cmd: str) -> RelayState:
|
||||||
state = True if cmd.find("ON") > -1 else False
|
state = True if cmd.find("ON") > -1 else False #TODO maybe better handling of other
|
||||||
#state = False if cmd.find("OFF") > -1 and state == None else None
|
#state = False if cmd.find("OFF") > -1 and state == None else None
|
||||||
|
id = None
|
||||||
r_state = RelayState(0, state)
|
|
||||||
id = 0
|
|
||||||
|
|
||||||
if cmd.startswith("T"):
|
if cmd.startswith("T"):
|
||||||
id = int(cmd[1]) + (8 if cmd[2] == "B" else 0)
|
id = int(cmd[1]) + (8 if cmd[2] == "B" else 0)
|
||||||
elif cmd.startswith("re_"):
|
elif cmd.startswith("re_"):
|
||||||
id = int(cmd[3]) + (8 if cmd[4] == "B" else 0)
|
id = int(cmd[3]) + (8 if cmd[4] == "B" else 0)
|
||||||
|
else:
|
||||||
|
id = None
|
||||||
r_state = RelayState(id, state)
|
r_state = RelayState(id, state)
|
||||||
return r_state
|
return r_state
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ noviBarcos = barcos.get('novi', None)
|
||||||
stariBarcos = barcos.get('stari', None)
|
stariBarcos = barcos.get('stari', None)
|
||||||
|
|
||||||
if noviBarcos:
|
if noviBarcos:
|
||||||
|
|
||||||
for k,v in noviBarcos.items():
|
for k,v in noviBarcos.items():
|
||||||
print(f'barco_novi@{k}.service') #to rabjo bit symlinki
|
print(f'barco_novi@{k}.service') #to rabjo bit symlinki
|
||||||
os.symlink('./barco@.service', f'./barco@{k}.service')
|
os.symlink('./barco@.service', f'./barco@{k}.service')
|
Loading…
Reference in a new issue