Compare commits
15 commits
franga2000
...
main
Author | SHA1 | Date | |
---|---|---|---|
b46ba4fe84 | |||
717340a3dd | |||
ff67bff7b8 | |||
c763b962ee | |||
36da67f369 | |||
b7ce9d850d | |||
29b2beca5a | |||
64a10b0512 | |||
c2d277663f | |||
![]() |
c1325c0eda | ||
![]() |
acc38950e7 | ||
![]() |
d00651a66c | ||
![]() |
61a8aa8ebc | ||
![]() |
6aacdcadbc | ||
a9820001b4 |
106 changed files with 4041 additions and 1887 deletions
35
README.md
35
README.md
|
@ -0,0 +1,35 @@
|
|||
# Multimedijski krmilnik FRI
|
||||
|
||||
> *Also known as **PolžProjekt** or **KatjaKontroler***
|
||||
|
||||
## Arhitektura
|
||||
|
||||
Sistem sestavljata dva kartična računalnika (trenutno Raspberry Pi 4) - *krmilnik* in *zaslon*. Na krmilniku je zagnan MQTT broker (mosquitto) in več *driverjev*, ki služijo kot mostovi med napravami v predavalnici in MQTT. Na zaslonu teče spletni brskalnik (chromium) z odprtim *frontendom* (VueJS aplikacija, servirana s krmilnika). Frontend komunicira neposredno z MQTT brokerjem preko WebSockets povezave.
|
||||
|
||||
### Driverji
|
||||
|
||||
- `tse_serial` - relejno polje *TSE ___*, ki krmili napajanje različnih komponent predavalnice, projekcijska platna in senčila
|
||||
- `barco_telnet` - en projektor tipa *Barco G62* preko novega "Barco Telnet" protokola. Za več projektorjev je potrebnih več instanc driverja
|
||||
- `projector_motors` - dvigala za projektorje preko dveh modulov *Grove I2C 4-channel SPDT relay*
|
||||
- `lucke` - razsvetljava v predavalnici preko A-rosso sistema WebSCADA
|
||||
- `extron_audio_matrix` - avdio matrika - **NI DOKONČAN**
|
||||
- `barco_rlmw_{http,tcp}` - en projektor serije *Barco RLM W*. Obstajata dve različici (http in tcp)
|
||||
|
||||
## Namestitev
|
||||
|
||||
Namestitev poteka z orodjem *Ansible*, potrebna pa je tudi lokalna namestitev orodja `npm` (za izgradnjo frontenda). V mapi `ansible_deploy` sta ločeni mapi z playbooki za krmilnik in zaslon.
|
||||
|
||||
Osnovni operacijski sistem za krmilnik je *RaspberryPi OS Lite* za zaslon pa *RaspberryPi OS Full*. Namestite ga lahko z orodjem *Raspberry Pi Imager* - pri tem bodite pozorni da nastavite uporabniško ime `pi` in geslo, ki je shranjeno v RC.
|
||||
|
||||
Za namestitev mora imeti Raspberry Pi dostop do interneta, zato ga priključite v omrežje z DHCP. Njegov IP naslov se izpiše na HDMI izhodu, lahko ga pa najdete tudi na drug način. Ker se po prvem zagonu na vgrajeni mrežni kartici konfigurira statičen IP naslov, je priporočeno, da se za namestitev uporablja USB Ethernet adapter.
|
||||
|
||||
Primer namestitve krmilnika za predavalnico P01 (ime v inventory datoteki je `p01_controller`), ki ima trenutno IP naslov `10.32.50.123` (ker se razlikuje od inventory datoteke ga je treba določiti z `-e ansible_host=`):
|
||||
|
||||
```sh
|
||||
cd ansible_deploy/controller
|
||||
ansible-playbook playbook.yml -i inventory.yml -l p01_controller -e ansible_host=10.32.50.123
|
||||
```
|
||||
|
||||
## Posodobitev
|
||||
|
||||
Posodobitev poteka enako kot namestitev. Če se izvaja v predavalnici je potrebo poskrbeti, da imajo ciljne naprave dostop do interneta. Za to ni dobrega načina, zato je priporočen enak pristop kot pri namestitvi (USB Ethernet adapter in omrežje z DHCP - lahko celo deljena povezava s prenosnika).
|
|
@ -1,6 +0,0 @@
|
|||
all:
|
||||
hosts:
|
||||
mmctrl-p1:
|
||||
projectors:
|
||||
- projector-p1-center
|
||||
- projector-p1-side
|
|
@ -1,30 +0,0 @@
|
|||
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 %}
|
16
ansible_deploy/controller/controller_script.service.j2
Normal file
16
ansible_deploy/controller/controller_script.service.j2
Normal file
|
@ -0,0 +1,16 @@
|
|||
[Unit]
|
||||
Description={{ script_file }}
|
||||
After=multi-user.target
|
||||
PartOf=mm-controller.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/poetry run python3 -u {{ script_file }}
|
||||
Type=simple
|
||||
Restart=always
|
||||
User=pi
|
||||
Group=pi
|
||||
RestartSec=10
|
||||
WorkingDirectory={{ INSTALL_BASE }}
|
||||
|
||||
[Install]
|
||||
WantedBy=mm-controller.target
|
82
ansible_deploy/controller/inventory.yml
Normal file
82
ansible_deploy/controller/inventory.yml
Normal file
|
@ -0,0 +1,82 @@
|
|||
predavalnice_pi:
|
||||
|
||||
# Skupne spremenljivke za vse krmilnike
|
||||
vars:
|
||||
|
||||
ansible_user: pi
|
||||
|
||||
# Multimedia network
|
||||
static_ip_cidr: "24"
|
||||
static_ip_gateway: "192.168.192.1"
|
||||
static_ip_dns_servers:
|
||||
- "212.235.188.28"
|
||||
- "212.235.188.29"
|
||||
|
||||
# Lokalni MQTT
|
||||
mqtt_ip: localhost
|
||||
mqtt_port: 1883
|
||||
|
||||
# Nastavitve za vse posamezne predavalnice
|
||||
hosts:
|
||||
|
||||
p01_controller:
|
||||
room: P01
|
||||
ansible_host: 192.168.192.42
|
||||
static_ip: 192.168.192.42
|
||||
|
||||
# Novi barco projektorji
|
||||
barco_G62:
|
||||
- position: main
|
||||
model: barco_G62
|
||||
port: 3023
|
||||
ip: 192.168.192.12
|
||||
- position: side
|
||||
model: barco_G62
|
||||
port: 3023
|
||||
ip: 192.168.192.13
|
||||
|
||||
tse_box:
|
||||
|
||||
projector_motors:
|
||||
- position: main
|
||||
i2c_address: 0x42
|
||||
offset: 0 #for when using single 8 channel relay board
|
||||
- position: side
|
||||
i2c_address: 0x43
|
||||
offset: 4
|
||||
|
||||
lucke:
|
||||
url: http://192.168.190.90:8091
|
||||
roomId: 1
|
||||
bearer_token: 0954afe1-4111-4f89-a123-fea08a55dc46
|
||||
|
||||
p22_controller:
|
||||
room: P22
|
||||
ansible_host: 192.168.192.43
|
||||
static_ip: 192.168.192.43
|
||||
|
||||
# Novi barco projektorji
|
||||
barco_G62:
|
||||
- position: main
|
||||
model: barco_G62
|
||||
port: 3023
|
||||
ip: 192.168.192.22
|
||||
- position: side
|
||||
model: barco_G62
|
||||
port: 3023
|
||||
ip: 192.168.192.23
|
||||
|
||||
tse_box:
|
||||
|
||||
projector_motors:
|
||||
- position: main
|
||||
i2c_address: 0x42
|
||||
offset: 0 #for when using single 8 channel relay board
|
||||
- position: side
|
||||
i2c_address: 0x43
|
||||
offset: 4
|
||||
|
||||
lucke:
|
||||
url: http://192.168.190.90:8091
|
||||
roomId: 2
|
||||
bearer_token: b44c8cdc-d848-4b49-9deb-79023a60a62a
|
38
ansible_deploy/controller/malinaConfig.toml.j2
Normal file
38
ansible_deploy/controller/malinaConfig.toml.j2
Normal file
|
@ -0,0 +1,38 @@
|
|||
[global]
|
||||
room = "{{ room }}"
|
||||
mqttHost = "{{ mqtt_ip }}"
|
||||
mqttPort = {{ mqtt_port }}
|
||||
|
||||
{% if barco_G62 is defined %}
|
||||
{%+ for projector in barco_G62 +%}
|
||||
[{{projector.model}}.{{projector.position}}]
|
||||
ip = "{{projector.ip}}"
|
||||
port = {{projector.port}}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{%+ if tse_box is defined +%}
|
||||
{# serial_device = "{{ tse_box.serial_device }}" #}
|
||||
{% endif %}
|
||||
|
||||
{%+ if projector_motors is defined +%} {# change to appropriate thingy for running them #}
|
||||
{%+ for motor in projector_motors +%}
|
||||
[projector_motors.{{motor.position}}]
|
||||
i2c_address = {{motor.i2c_address}}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{%+ if extron_audio is defined +%}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{%+ if extron_video is defined +%}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{%+ if lucke is defined +%}
|
||||
[lucke]
|
||||
url = "{{ lucke.url }}"
|
||||
roomId = "{{ lucke.roomId }}"
|
||||
bearer_token = "{{ lucke.bearer_token }}"
|
||||
{% endif %}
|
3
ansible_deploy/controller/mm-controller.target.j2
Normal file
3
ansible_deploy/controller/mm-controller.target.j2
Normal file
|
@ -0,0 +1,3 @@
|
|||
[Unit]
|
||||
Description=Krmilnik multimedijskega sistema
|
||||
After=multi-user.target
|
|
@ -472,7 +472,7 @@ allow_anonymous true
|
|||
# Note that if the broker is running as a Windows service it will default to
|
||||
# "log_dest none" and neither stdout nor stderr logging is available.
|
||||
# Use "log_dest none" if you wish to disable logging.
|
||||
#log_dest stderr
|
||||
log_dest syslog
|
||||
|
||||
# Types of messages to log. Use multiple log_type lines for logging
|
||||
# multiple types of messages.
|
236
ansible_deploy/controller/playbook.yml
Normal file
236
ansible_deploy/controller/playbook.yml
Normal file
|
@ -0,0 +1,236 @@
|
|||
- name: Test playbook
|
||||
vars:
|
||||
PROJECT_BASE: "{{playbook_dir}}/../../"
|
||||
INSTALL_BASE: "/home/pi/pyServices"
|
||||
|
||||
hosts:
|
||||
- p01_controller
|
||||
- p22_controller
|
||||
|
||||
handlers:
|
||||
- name: restart NetworkManager
|
||||
ansible.builtin.service:
|
||||
name: NetworkManager
|
||||
state: restarted
|
||||
- name: Restart mosquitto
|
||||
become: true
|
||||
ansible.builtin.systemd_service:
|
||||
name: mosquitto.service
|
||||
state: restarted
|
||||
|
||||
tasks:
|
||||
|
||||
#
|
||||
# NETWORK CONFIGURATION
|
||||
#
|
||||
|
||||
- name: set eth0 static IP
|
||||
become: true
|
||||
community.general.nmcli:
|
||||
conn_name: "Multimedia network"
|
||||
ifname: eth0
|
||||
type: ethernet
|
||||
ip4: "{{ static_ip }}/{{ static_ip_cidr }}"
|
||||
# Sorry timi
|
||||
method6: disabled
|
||||
# Multimedia net doesn't have Internet access, so this iface shouldn't be used for Internet access
|
||||
never_default4: true
|
||||
routes4_extended:
|
||||
- ip: 192.168.0.0/16
|
||||
next_hop: "{{ static_ip_gateway }}"
|
||||
metric: 9999
|
||||
- ip: 10.0.0.0/8
|
||||
next_hop: "{{ static_ip_gateway }}"
|
||||
metric: 9999
|
||||
# gw4: "{{ static_ip_gateway }}"
|
||||
state: present
|
||||
|
||||
|
||||
#
|
||||
# SYSTEM DEPENDENCIES
|
||||
#
|
||||
- name: Install pkgs
|
||||
become: true
|
||||
apt:
|
||||
name:
|
||||
- python3-pip
|
||||
- mosquitto
|
||||
- nginx
|
||||
state: latest
|
||||
update_cache: true
|
||||
|
||||
- name: Install Poetry
|
||||
become: true
|
||||
pip:
|
||||
break_system_packages: true
|
||||
name:
|
||||
- poetry
|
||||
#
|
||||
# MOSQUITTO
|
||||
#
|
||||
- name: mosquitto enable
|
||||
become: true
|
||||
ansible.builtin.systemd_service:
|
||||
name: mosquitto.service
|
||||
enabled: true
|
||||
|
||||
- name: Copy mosqitconfig
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
src: mosquitto.conf
|
||||
dest: /etc/mosquitto/mosquitto.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
backup: yes
|
||||
notify: Restart mosquitto
|
||||
|
||||
#
|
||||
# INSTALL CONTROLLER
|
||||
#
|
||||
|
||||
- name: Create installation directory
|
||||
file:
|
||||
path: "{{INSTALL_BASE}}"
|
||||
state: directory
|
||||
|
||||
- name: Install controller scripts
|
||||
ansible.posix.synchronize:
|
||||
src: "{{PROJECT_BASE}}/controller/"
|
||||
dest: "{{INSTALL_BASE}}/"
|
||||
delete: true
|
||||
archive: false
|
||||
recursive: true
|
||||
|
||||
- name: template config.toml
|
||||
ansible.builtin.template:
|
||||
src: ./malinaConfig.toml.j2
|
||||
dest: "{{INSTALL_BASE}}/malinaConfig.toml"
|
||||
|
||||
- name: Install python libraries
|
||||
ansible.builtin.shell:
|
||||
cmd: "poetry install"
|
||||
chdir: "{{INSTALL_BASE}}"
|
||||
|
||||
|
||||
#
|
||||
# INSTALL FRONTEND
|
||||
#
|
||||
|
||||
- name: Build frontend (localhost)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell:
|
||||
cmd: "npm install --dev && npm run build"
|
||||
chdir: "{{PROJECT_BASE}}/frontend"
|
||||
|
||||
- name: Install frontend
|
||||
become: true
|
||||
ansible.posix.synchronize:
|
||||
src: "{{PROJECT_BASE}}/frontend/dist/"
|
||||
dest: "/var/www/html/"
|
||||
delete: true
|
||||
archive: false
|
||||
recursive: true
|
||||
|
||||
- name: Fix www root permission
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: "/var/www/html"
|
||||
owner: pi
|
||||
group: pi
|
||||
recurse: true
|
||||
mode: 'u=rwX,g=rX,o=rX'
|
||||
|
||||
#
|
||||
# SERVICES
|
||||
#
|
||||
|
||||
- name: template target
|
||||
become: true
|
||||
ansible.builtin.template:
|
||||
src: ./mm-controller.target.j2
|
||||
dest: /etc/systemd/system/mm-controller.target
|
||||
|
||||
- name: Generate systemd services
|
||||
become: true
|
||||
block:
|
||||
# Barco G62 (novi projektor)
|
||||
- name: Barco G62 services
|
||||
when: barco_G62 is defined
|
||||
block:
|
||||
- name: template service
|
||||
become: true
|
||||
vars:
|
||||
script_file: "{{INSTALL_BASE}}/barco_telnet/barco_G62_control.py %i"
|
||||
ansible.builtin.template:
|
||||
src: ./controller_script.service.j2
|
||||
dest: /etc/systemd/system/barco@.service
|
||||
- name: enable service
|
||||
ansible.builtin.systemd_service:
|
||||
name: "{{item}}"
|
||||
enabled: true
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
loop:
|
||||
- barco@main.service
|
||||
- barco@side.service
|
||||
|
||||
# Lifti za projektorje (naši releji)
|
||||
- name: template projector motors service
|
||||
when: projector_motors is defined
|
||||
block:
|
||||
- name: template service
|
||||
become: true
|
||||
vars:
|
||||
script_file: "{{INSTALL_BASE}}/projector_motors/projector_motors.py"
|
||||
ansible.builtin.template:
|
||||
src: ./controller_script.service.j2
|
||||
dest: /etc/systemd/system/projector_motors.service
|
||||
- name: enable service
|
||||
ansible.builtin.systemd_service:
|
||||
name: projector_motors.service
|
||||
enabled: true
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
|
||||
# Power, platna, etc. (TSE relay box)
|
||||
- name: template tse serial box service
|
||||
when: tse_box is defined
|
||||
block:
|
||||
- name: template service
|
||||
become: true
|
||||
vars:
|
||||
script_file: "{{INSTALL_BASE}}/tse_serial/tse_serial_controler.py"
|
||||
ansible.builtin.template:
|
||||
src: ./controller_script.service.j2
|
||||
dest: /etc/systemd/system/tse_box.service
|
||||
- name: enable service
|
||||
ansible.builtin.systemd_service:
|
||||
name: tse_box.service
|
||||
enabled: true
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
|
||||
# a-rosso lučke kontroler
|
||||
- name: template lucke service
|
||||
when: lucke is defined
|
||||
block:
|
||||
- name: tmeplate service
|
||||
become: true
|
||||
vars:
|
||||
script_file: "{{INSTALL_BASE}}/lucke/luckeControl.py"
|
||||
ansible.builtin.template:
|
||||
src: ./controller_script.service.j2
|
||||
dest: /etc/systemd/system/lucke.service
|
||||
- name: enable service
|
||||
ansible.builtin.systemd_service:
|
||||
name: lucke.service
|
||||
enabled: true
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
|
||||
|
||||
- name: daemon reload
|
||||
become: true
|
||||
ansible.builtin.systemd_service:
|
||||
daemon_reload: true
|
27
ansible_deploy/display/inventory.yml
Normal file
27
ansible_deploy/display/inventory.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
predavalnice_pi:
|
||||
# Skupne spremenljivke za vse zaslone
|
||||
vars:
|
||||
ansible_user: pi
|
||||
|
||||
# Check here: https://github.com/leukipp/touchkio/releases
|
||||
touchkio_version: "1.3.1"
|
||||
# Multimedia network
|
||||
static_ip_cidr: "24"
|
||||
static_ip_gateway: "192.168.192.1"
|
||||
static_ip_dns_servers:
|
||||
- "212.235.188.28"
|
||||
- "212.235.188.29"
|
||||
hosts:
|
||||
p01_touch_display:
|
||||
predavalnica: p01
|
||||
static_ip: "192.168.192.111"
|
||||
hostname: "p01_touch_display.local"
|
||||
kiosk_url: "http://192.168.192.42?room=P01"
|
||||
mqtt_host: "192.168.192.42"
|
||||
|
||||
p22_touch_display:
|
||||
predavalnica: p22
|
||||
static_ip: "192.168.192.112"
|
||||
hostname: "p22_touch_display.local"
|
||||
kiosk_url: "http://192.168.192.43?room=P22"
|
||||
mqtt_host: "192.168.192.43"
|
|
@ -1,3 +1,10 @@
|
|||
|
||||
# - name: Change splash image
|
||||
# become: true
|
||||
# copy:
|
||||
# src: splash.png
|
||||
# dest: /usr/share/plymouth/themes/pix/splash.png
|
||||
|
||||
- name: Enable boot splash screen
|
||||
become: true
|
||||
shell: "raspi-config nonint get_boot_splash && raspi-config nonint do_boot_splash 0"
|
||||
|
@ -13,12 +20,6 @@
|
|||
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:
|
|
@ -2,5 +2,6 @@
|
|||
# TODO: better include (import playbook)
|
||||
tasks:
|
||||
- include_tasks: static_ip.yml
|
||||
# - include_tasks: wifi_temp.yml # TODO: remove this when we don't need wifi anymore
|
||||
- include_tasks: pi_stuff.yml
|
||||
- include_tasks: touch_display.yml
|
32
ansible_deploy/display/static_ip.yml
Normal file
32
ansible_deploy/display/static_ip.yml
Normal file
|
@ -0,0 +1,32 @@
|
|||
- name: Configure static IP address (using Network Manager)
|
||||
become: yes
|
||||
community.general.nmcli:
|
||||
conn_name: "Multimedia net"
|
||||
ifname: eth0
|
||||
type: ethernet
|
||||
ip4: "{{ static_ip }}/{{ static_ip_cidr }}"
|
||||
# Sorry timi
|
||||
method6: disabled
|
||||
# Multimedia net doesn't have Internet access, so this iface shouldn't be used for Internet access
|
||||
never_default4: true
|
||||
routes4_extended:
|
||||
- ip: 192.168.0.0/16
|
||||
next_hop: "{{ static_ip_gateway }}"
|
||||
metric: 9999
|
||||
- ip: 10.0.0.0/8
|
||||
next_hop: "{{ static_ip_gateway }}"
|
||||
metric: 9999
|
||||
# gw4: "{{ static_ip_gateway }}"
|
||||
state: present
|
||||
conn_reload: true
|
||||
|
||||
- name: Wait for network to be available
|
||||
become: yes
|
||||
wait_for_connection:
|
||||
timeout: 60
|
||||
|
||||
- name: Display new IP configuration
|
||||
debug:
|
||||
msg: "Static IP configured: {{ static_ip }}/{{ static_ip_cidr }}"
|
||||
|
||||
|
|
@ -1,14 +1,20 @@
|
|||
- name: Fix fonts
|
||||
become: true
|
||||
apt:
|
||||
name:
|
||||
- fonts-noto-core
|
||||
state: present
|
||||
|
||||
- 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"
|
||||
dest: "/home/pi/touchkio_{{ touchkio_version }}_arm64.deb"
|
||||
register: deb_download
|
||||
|
||||
- name: Install the latest .deb package
|
||||
become: yes
|
||||
apt:
|
||||
deb: "/tmp/touchkio_{{ touchkio_version }}_arm64.deb"
|
||||
deb: "/home/pi/touchkio_{{ touchkio_version }}_arm64.deb"
|
||||
when: deb_download is succeeded
|
||||
|
||||
- name: Create systemd user service directory
|
12
ansible_deploy/display/touchkio.service.j2
Normal file
12
ansible_deploy/display/touchkio.service.j2
Normal file
|
@ -0,0 +1,12 @@
|
|||
[Unit]
|
||||
Description=Kiosk browser
|
||||
After=graphical.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/touchkio --web-url="{{ kiosk_url }}" --web-zoom=1.0 "--mqtt-url=mqtt://{{ mqtt_host }}"
|
||||
#ExecStart=/usr/bin/chromium-browser --ozone-platform=x11 --noerrdialogs --disable-infobars --kiosk --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 --app="{{ kiosk_url }}"
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
26
ansible_deploy/display/wifi_temp.yml
Normal file
26
ansible_deploy/display/wifi_temp.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
- name: Set wifi country
|
||||
become: true
|
||||
shell: "raspi-config nonint get_wifi_country && raspi-config nonint do_wifi_country SI; true"
|
||||
register: wifi_country
|
||||
changed_when:
|
||||
- wifi_country != "SI"
|
||||
|
||||
- name: Configure P2P wifi
|
||||
become: yes
|
||||
community.general.nmcli:
|
||||
conn_name: "MALINCA"
|
||||
ifname: wlan0
|
||||
type: wifi
|
||||
ssid: "MALINCA"
|
||||
wifi_sec:
|
||||
key-mgmt: wpa-psk
|
||||
psk: "MALINCA123"
|
||||
autoconnect: true
|
||||
method4: auto
|
||||
state: present
|
||||
|
||||
- name: Restart NetworkManager
|
||||
become: yes
|
||||
service:
|
||||
name: NetworkManager
|
||||
state: restarted
|
|
@ -1,4 +0,0 @@
|
|||
[prHosts]
|
||||
192.168.122.245
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
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:
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
- 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
|
|
@ -1,15 +0,0 @@
|
|||
[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
|
|
@ -1,37 +0,0 @@
|
|||
- 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 +0,0 @@
|
|||
[Unit]
|
||||
Description=Barco projector control
|
||||
After=mqtt_init.service
|
||||
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/python3 /home/rpi/barco_telnet_control.py
|
||||
User=rpi
|
||||
Group=rpi
|
||||
Type=simple
|
||||
Restart=always
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=mqtt_init.service
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
[Unit]
|
||||
Description=Barco projector control
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/poetry run python3 /home/kat/pyServices/fri-mm-maline/barco_G62_control.py %i
|
||||
Type=simple
|
||||
Restart=always
|
||||
WorkingDirectory=/home/kat/pyServices/fri-mm-maline
|
||||
User=kat
|
||||
Group=kat
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
DefaultInstance=main
|
|
@ -1,127 +0,0 @@
|
|||
import asyncio
|
||||
import socket
|
||||
import aiomqtt
|
||||
import telnetlib3
|
||||
import toml
|
||||
import sys
|
||||
|
||||
#GLOBALS
|
||||
|
||||
room = "undefined"
|
||||
position = "undefined"
|
||||
barcoIP = "undefined"
|
||||
telnetPort = 3023
|
||||
mqttPort = 1883
|
||||
mqttIP = 'localhost'
|
||||
|
||||
cmdMap = {
|
||||
'power': 'POWR',
|
||||
'shutter': 'PMUT',
|
||||
'freeze': 'FRZE'
|
||||
}
|
||||
reverseCmdMap = {v: k for k, v in cmdMap.items()}
|
||||
|
||||
|
||||
def parse_barco_response(raw: str):
|
||||
raw = raw[1:-1] # strip square brackets
|
||||
#print(raw)
|
||||
if raw.startswith("ERR"):
|
||||
return None # TODO parse type error - "disabled control" is special case which shouldnt normally happen
|
||||
cmd, status = raw.split("!", maxsplit=2)
|
||||
#print(cmd + " " + status)
|
||||
cmd = reverseCmdMap[cmd]
|
||||
status = '1' if status == '01' else '0'
|
||||
return cmd, status
|
||||
|
||||
#TODO MAKE THESE USE GLOBAL INSTEAD OF PASSING BARCO POSITION IN MAIN
|
||||
|
||||
async def barco_telnet_command(client, writer, select: str):
|
||||
global room
|
||||
onSub = f"{room}/projectors/{select}/#"
|
||||
#print('TEST', onSub)
|
||||
onMatch = f"{room}/projectors/{select}/command/+"
|
||||
await client.subscribe(onSub)
|
||||
#async with client.messages as msgs:
|
||||
async for mesg in client.messages:
|
||||
# print(mesg.topic.value)
|
||||
# print(mesg.payload.decode())
|
||||
# print('on', select)
|
||||
|
||||
if mesg.topic.matches(onMatch):
|
||||
# print("test")
|
||||
cmd = mesg.topic.value.split("/")[-1]
|
||||
#val = '1' if mesg.payload.decode() == 'ON' else '0'
|
||||
val = '1' if mesg.payload.decode() == '1' else '0' # refactor to direct 0 and 1
|
||||
barcoCmd = f"[{cmdMap[cmd]}{val}]"
|
||||
print("Received: [" + mesg.topic.value + "] payload: [" + mesg.payload.decode() + "]")
|
||||
print("Sending command to Barco: " + barcoCmd)
|
||||
writer.write(barcoCmd)
|
||||
|
||||
|
||||
async def barco_telnet_read_status(client, reader, select: str):
|
||||
global room
|
||||
while True:
|
||||
output = await reader.readuntil(b']')
|
||||
raw_response: str = output.decode().strip() # strip not necessary? needed for local netcat testing though
|
||||
print("Received: " + raw_response + " from Barco (" + select + ')')
|
||||
parsed = parse_barco_response(raw_response)
|
||||
if parsed == None:
|
||||
continue #TODO alert for errors
|
||||
print(f"Updating topic [{parsed[0]}] with value [{parsed[1]}]")
|
||||
await client.publish(f"{room}/projectors/{select}/status/{parsed[0]}", payload=parsed[1])
|
||||
|
||||
|
||||
async def barco_telnet_query_status(writer, select: str):
|
||||
while True:
|
||||
for val in cmdMap.values():
|
||||
print(f"Querying Barco {select} with: [{val}?]")
|
||||
writer.write(f"[{val}?]" + '\r\n') # TODO test if funny CRLF necessary (it probably gets ignored)
|
||||
await asyncio.sleep(0.2) # sleep between writes necessary, otherwise it gets confused.
|
||||
# simultaneous commands from control could break this? TODO fix later
|
||||
await asyncio.sleep(30) # TODO find appropriate period
|
||||
|
||||
|
||||
# async def shell(reader, writer):
|
||||
# async with aiomqtt.Client('localhost', 1883) as client:
|
||||
# task_status_query = asyncio.create_task(barco_telnet_query_status(writer))
|
||||
# task_status_reader = asyncio.create_task(barco_telnet_read_status(client, reader))
|
||||
# task_control = asyncio.create_task(barco_telnet_command(client, writer))
|
||||
# await asyncio.gather(task_status_query, task_status_reader, task_control)
|
||||
|
||||
|
||||
async def main():
|
||||
global barcoPosition, barcoIP, telnetPort, mqttIP, mqttPort
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit("No position provided")
|
||||
else:
|
||||
barcoPosition = sys.argv[1]
|
||||
|
||||
conf = toml.load('./config.toml')
|
||||
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
|
||||
|
||||
# if __name__ == '__main__':
|
||||
|
||||
# loop = asyncio.get_event_loop()
|
||||
# coro = telnetlib3.open_connection(mainBarcoIP, 3023, shell=shell)
|
||||
# coro = telnetlib3.open_connection(mainBarcoIP, 3023, shell=shell)
|
||||
# # coro = telnetlib3.open_connection('localhost', 1234, shell=shell)
|
||||
# reader, writer = loop.run_until_complete(coro)
|
||||
# loop.run_until_complete(writer.protocol.waiter_closed)
|
||||
|
||||
#mqttIP = sys.argv[1]
|
||||
#barcoIP = sys.argv[2]
|
||||
|
||||
asyncio.run(main())
|
14
config.toml
14
config.toml
|
@ -1,14 +0,0 @@
|
|||
[globals]
|
||||
room = 'P01'
|
||||
mqttIp = 'localhost'
|
||||
mqttPort = '1883'
|
||||
|
||||
|
||||
[barco.novi.glavni]
|
||||
ip = '192.168.192.12'
|
||||
port = '3023'
|
||||
|
||||
[barco.novi.stranski]
|
||||
ip = '192.168.192.16' # or smth
|
||||
port = '3023'
|
||||
|
67
controller/barco_rlmw_http/barco_rlwm_http.py
Normal file
67
controller/barco_rlmw_http/barco_rlwm_http.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
import re
|
||||
import asyncio
|
||||
import aiohttp
|
||||
from asyncio.exceptions import CancelledError
|
||||
|
||||
PORT = 43680
|
||||
RE_STATUS = re.compile(r".stats.txt(.+).value='(.+)';")
|
||||
REMOTEKEY_TIMEOUT = aiohttp.ClientTimeout(total=1)
|
||||
|
||||
|
||||
class BarcoRLM_Control:
|
||||
def __init__(self, projector_ip):
|
||||
self.projector_ip = projector_ip
|
||||
self.session = None
|
||||
|
||||
async def _request(self, method, path, *args, **kwargs):
|
||||
if not self.session:
|
||||
self.session = aiohttp.ClientSession()
|
||||
url = f"http://{self.projector_ip}{path}"
|
||||
resp = await self.session.request(method, url, *args, **kwargs)
|
||||
resp.raise_for_status()
|
||||
return await resp.text()
|
||||
|
||||
async def get_status(self):
|
||||
resp = await self._request("GET", "/tgi/firststatus.tgi")
|
||||
matches = RE_STATUS.findall(resp)
|
||||
status = dict(matches)
|
||||
return status
|
||||
|
||||
async def toggle_power(self):
|
||||
await self._request("GET", "/tgi/general.tgi?powertog")
|
||||
|
||||
async def click_remote(self, key):
|
||||
try:
|
||||
await self._request("GET", f"/tgi/remote.tgi?{key}", timeout=REMOTEKEY_TIMEOUT, raise_for_status=False)
|
||||
except TimeoutError:
|
||||
pass
|
||||
|
||||
async def set_shutter(self, shutter):
|
||||
endpoint = f"/tgi/general.tgi?pause{onoff(shutter)}"
|
||||
await self._request("GET", endpoint)
|
||||
|
||||
async def set_power(self, power):
|
||||
key = onoff(power) + "ky"
|
||||
await self.click_remote(key)
|
||||
|
||||
async def set_input(self, input):
|
||||
endpoint = f"/tgi/input.tgi?{input}"
|
||||
await self._request("GET", endpoint)
|
||||
|
||||
|
||||
def onoff(state: bool) -> str:
|
||||
if state:
|
||||
return "on"
|
||||
else:
|
||||
return "off"
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
async def main():
|
||||
barco = BarcoRLM_Control("192.168.192.12")
|
||||
status = await barco.get_status()
|
||||
print(status)
|
||||
|
||||
await barco.click_remote("kymenu")
|
||||
|
||||
asyncio.run(main())
|
80
controller/barco_rlmw_http/main.py
Normal file
80
controller/barco_rlmw_http/main.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
import asyncio
|
||||
import aiomqtt
|
||||
from collections import defaultdict
|
||||
|
||||
from barco_legacy import BarcoRLM_Control
|
||||
|
||||
# TODO MAKE THIS CONFIGURALBE
|
||||
PROJECTOR_IP = "192.168.192.12"
|
||||
MQTT_PREFIX = "p22/projektorji/glavni"
|
||||
MQTT_HOST = "-----"
|
||||
POLLING_PERIOD_SEC = 10
|
||||
|
||||
|
||||
class BarcoRLM_MQTT:
|
||||
def __init__(self, projector_ip, mqtt_prefix):
|
||||
self.projector_ip = projector_ip
|
||||
self.mqtt_prefix = mqtt_prefix
|
||||
self.barco = BarcoRLM_Control(projector_ip)
|
||||
self.last_status = defaultdict(lambda: '')
|
||||
|
||||
async def run(self):
|
||||
async with aiomqtt.Client(MQTT_HOST, 1883) as client:
|
||||
self.client = client
|
||||
task_polling = asyncio.create_task(self.task_polling())
|
||||
task_control = asyncio.create_task(self.task_control())
|
||||
await asyncio.gather(task_control, task_polling)
|
||||
|
||||
async def task_control(self):
|
||||
topicMatch = f"{self.mqtt_prefix}/ukaz/+"
|
||||
await self.client.subscribe(topicMatch)
|
||||
|
||||
async for mesg in self.client.messages:
|
||||
cmd = mesg.topic.value.split("/")[-1]
|
||||
val = mesg.payload.decode()
|
||||
|
||||
if cmd == "power":
|
||||
await self.barco.set_power(onoff(val))
|
||||
elif cmd == "shutter":
|
||||
await self.barco.set_shutter(onoff(val))
|
||||
|
||||
async def task_polling(self):
|
||||
while True:
|
||||
status = await self.barco.get_status()
|
||||
for key, val in status.items():
|
||||
if self.last_status[key] != val:
|
||||
print(f"Status change {key}={val}")
|
||||
await self.on_status_change(key, val)
|
||||
|
||||
self.last_status = status
|
||||
await asyncio.sleep(POLLING_PERIOD_SEC)
|
||||
|
||||
async def on_status_change(self, key, val):
|
||||
mkey = mval = None
|
||||
if key == "status":
|
||||
mkey = "power"
|
||||
if val == "Imaging":
|
||||
mval = "1"
|
||||
if val == "Standby":
|
||||
mval = "0"
|
||||
if key == "src":
|
||||
mkey = "input"
|
||||
mval = val
|
||||
if key == "fmr":
|
||||
mkey = "input_format"
|
||||
mval = val
|
||||
if mkey is not None and mval is not None:
|
||||
await self.client.publish(f"{self.mqtt_prefix}/status/{mkey}", payload=mval)
|
||||
|
||||
|
||||
def onoff(input):
|
||||
#if input == "1":
|
||||
# return True
|
||||
#elif input == "0":
|
||||
# return False
|
||||
return input == "1"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
barco = BarcoRLM_MQTT(PROJECTOR_IP, MQTT_PREFIX)
|
||||
asyncio.run(barco.run())
|
98
controller/barco_rlmw_tcp/barco_rlmw_tcp.py
Normal file
98
controller/barco_rlmw_tcp/barco_rlmw_tcp.py
Normal file
|
@ -0,0 +1,98 @@
|
|||
import re
|
||||
import asyncio
|
||||
import telnetlib3
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
PORT = 43680
|
||||
RE_STATUS = re.compile(r"OP (.+?)(?:$| = (.+)$)")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ACK:
|
||||
command: str
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ValueUpdate:
|
||||
key: str
|
||||
value: str
|
||||
|
||||
class Status(StrEnum):
|
||||
standby = "0"
|
||||
warmup = "1"
|
||||
imaging = "2"
|
||||
cooling = "3"
|
||||
warning = "4"
|
||||
|
||||
class LampMode(StrEnum):
|
||||
economy = "0"
|
||||
standard = "1"
|
||||
dimming = "2"
|
||||
|
||||
class ValueType(StrEnum):
|
||||
status = "status"
|
||||
picture_mute = "picture.mute"
|
||||
lamp_mode = "lamp.mode"
|
||||
|
||||
|
||||
class BarcoRLMW_TCP:
|
||||
def __init__(self, projector_ip):
|
||||
self.projector_ip = projector_ip
|
||||
self.session = None
|
||||
|
||||
async def init(self):
|
||||
self.reader, self.writer = await telnetlib3.open_connection(self.projector_ip, 3023)
|
||||
|
||||
async def exec(self, cmd):
|
||||
await self.writer.write("op " + cmd + "\r")
|
||||
|
||||
async def set(self, key, val):
|
||||
await self.writer.write("op " + key + " = " + val + "\r")
|
||||
|
||||
async def query(self, key):
|
||||
await self.writer.write("op " + key + " ?\r")
|
||||
|
||||
async def set_power(self, power):
|
||||
await self.exec(f"power.{onoff(power)}")
|
||||
|
||||
async def set_shutter(self, shutter):
|
||||
await self.set("picture.mute", int(shutter))
|
||||
|
||||
async def iter_messages(self):
|
||||
"""Async iterator that processes feedback from the projector.
|
||||
Yields ACK (acknowledgement) and ValueUpdate (value has changed) messages."""
|
||||
while True:
|
||||
line = await self.reader.readuntil('\r').decode().strip()
|
||||
msg = self.parse_response(line)
|
||||
if msg:
|
||||
yield msg
|
||||
|
||||
def parse_response(self, line):
|
||||
matches = RE_STATUS.findall(line)
|
||||
|
||||
if len(matches) == 0:
|
||||
return None
|
||||
match = matches[0]
|
||||
|
||||
if len(match) == 2:
|
||||
return ACK(match[1])
|
||||
if len(match) == 3:
|
||||
return ValueUpdate(match[1], match[2])
|
||||
|
||||
|
||||
def onoff(state: bool) -> str:
|
||||
if state:
|
||||
return "on"
|
||||
else:
|
||||
return "off"
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
async def main():
|
||||
barco = BarcoRLMW_TCP("192.168.192.12")
|
||||
status = await barco.get_status()
|
||||
print(status)
|
||||
|
||||
await barco.click_remote("kymenu")
|
||||
|
||||
asyncio.run(main())
|
72
controller/barco_rlmw_tcp/main.py
Normal file
72
controller/barco_rlmw_tcp/main.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
import asyncio
|
||||
import aiomqtt
|
||||
from collections import defaultdict
|
||||
|
||||
from barco_rlmw_tcp import BarcoRLMW_TCP, ValueUpdate, Status, ACK, ValueType
|
||||
|
||||
# TODO MAKE THIS CONFIGURALBE
|
||||
PROJECTOR_IP = "192.168.192.12"
|
||||
MQTT_PREFIX = "p22/projektorji/glavni"
|
||||
MQTT_HOST = "-------"
|
||||
POLLING_PERIOD_SEC = 10
|
||||
|
||||
|
||||
class BarcoRLMW_TCP_MQTT:
|
||||
def __init__(self, projector_ip, mqtt_prefix):
|
||||
self.projector_ip = projector_ip
|
||||
self.mqtt_prefix = mqtt_prefix
|
||||
self.barco = BarcoRLMW_TCP(projector_ip)
|
||||
self.last_status = defaultdict(lambda: '')
|
||||
|
||||
async def run(self):
|
||||
async with aiomqtt.Client(MQTT_HOST, 1883) as client:
|
||||
self.client = client
|
||||
task_polling = asyncio.create_task(self.task_polling())
|
||||
task_control = asyncio.create_task(self.task_control())
|
||||
await asyncio.gather(task_control, task_polling)
|
||||
|
||||
async def task_control(self):
|
||||
topicMatch = f"{self.mqtt_prefix}/ukaz/+"
|
||||
await self.client.subscribe(topicMatch)
|
||||
|
||||
async for mesg in self.client.messages:
|
||||
cmd = mesg.topic.value.split("/")[-1]
|
||||
val = mesg.payload.decode()
|
||||
|
||||
if cmd == "power":
|
||||
await self.barco.set_power(onoff(val))
|
||||
elif cmd == "shutter":
|
||||
await self.barco.set_shutter(onoff(val))
|
||||
|
||||
async def task_polling(self):
|
||||
while True:
|
||||
await self.barco.query("status")
|
||||
await asyncio.sleep(POLLING_PERIOD_SEC)
|
||||
|
||||
async def write_status(self, status, value):
|
||||
await self.client.publish(f"{self.mqtt_prefix}/status/{status}", payload=value)
|
||||
|
||||
async def task_process_messages(self):
|
||||
async for msg in self.barco.iter_messages():
|
||||
# We only care about value updates
|
||||
if isinstance(msg, ValueUpdate):
|
||||
# Power status
|
||||
if msg.key == ValueType.status:
|
||||
if msg.value in (Status.imaging, Status.warmup):
|
||||
self.write_status("power", 1)
|
||||
elif msg.value in (Status.standby, Status.cooling):
|
||||
self.write_status("power", 0)
|
||||
# Video mute
|
||||
if msg.key == ValueType.picture_mute:
|
||||
self.write_status("shutter", msg.value)
|
||||
|
||||
def onoff(input):
|
||||
if input == "1":
|
||||
return True
|
||||
elif input == "0":
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
barco = BarcoRLMW_TCP_MQTT(PROJECTOR_IP, MQTT_PREFIX)
|
||||
asyncio.run(barco.run())
|
138
controller/barco_telnet/barco_G62_control.py
Normal file
138
controller/barco_telnet/barco_G62_control.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
import asyncio
|
||||
from collections import defaultdict
|
||||
import aiomqtt
|
||||
import telnetlib3
|
||||
import toml
|
||||
import sys
|
||||
import os
|
||||
|
||||
#GLOBALS
|
||||
|
||||
room: str
|
||||
barcoPosition: str
|
||||
barcoReached: bool
|
||||
|
||||
lastState = defaultdict(lambda: None)
|
||||
|
||||
cmdMap = {
|
||||
'power': 'POWR',
|
||||
'shutter': 'PMUT',
|
||||
'freeze': 'FRZE',
|
||||
#'test_pattern': 'TPRN',
|
||||
}
|
||||
reverseCmdMap = {v: k for k, v in cmdMap.items()}
|
||||
|
||||
# There needs to be a minimum time between writes. Since we have two "threads", we use a lock and a sleep in barco_send() to enforce it
|
||||
lock = asyncio.Lock()
|
||||
|
||||
def parse_barco_response(raw: str):
|
||||
raw = raw[1:-1] # strip square brackets
|
||||
|
||||
if raw.startswith("ERR"):
|
||||
print("ERROR:", raw)
|
||||
return None # TODO parse type error - "disabled control" is special case which shouldnt normally happen
|
||||
|
||||
cmd, status = raw.split("!", maxsplit=2)
|
||||
|
||||
cmd = reverseCmdMap[cmd]
|
||||
status = int(status)
|
||||
|
||||
barcoReached = True
|
||||
return cmd, status
|
||||
|
||||
|
||||
async def barco_send(writer, value):
|
||||
async with lock:
|
||||
writer.write(value + '\r\n')
|
||||
print("Writing", value)
|
||||
await asyncio.sleep(0.2) # sleep between writes necessary, otherwise it gets confused.
|
||||
|
||||
async def barco_telnet_command(client, writer, select: str):
|
||||
"""Receive commands from MQTT and send them to the projector"""
|
||||
await client.subscribe(f"{room}/projectors/{select}/#")
|
||||
|
||||
async for mesg in client.messages:
|
||||
|
||||
if mesg.topic.matches(f"{room}/projectors/{select}/set/+"):
|
||||
cmd = mesg.topic.value.rsplit("/", maxsplit=1)[-1]
|
||||
val = mesg.payload.decode()
|
||||
|
||||
if val not in ("0", "1") or cmd not in cmdMap:
|
||||
print("INVALID COMMAND OR VALUE:", cmd, val)
|
||||
continue
|
||||
|
||||
barcoCmd = cmdMap[cmd]
|
||||
# Send command to projector
|
||||
await barco_send(writer, f"[{barcoCmd}{val}]")
|
||||
# Immediately ask for a status
|
||||
await barco_send(writer, f"[{barcoCmd}?]")
|
||||
|
||||
|
||||
async def barco_telnet_read_status(client, reader, select: str):
|
||||
"""Read status reports (we trigger them in the polling task as well as whenver sending a command)"""
|
||||
while True:
|
||||
output = await reader.readuntil(b']')
|
||||
raw_response: str = output.decode()
|
||||
print("Received: " + raw_response + " from Barco (" + select + ')')
|
||||
|
||||
try:
|
||||
key, val = parse_barco_response(raw_response)
|
||||
except:
|
||||
print("NOT PARSED:", raw_response)
|
||||
continue
|
||||
|
||||
await client.publish(f"{room}/projectors/{select}/status/{key}", payload=val)
|
||||
|
||||
|
||||
async def barco_telnet_query_status(writer, select: str):
|
||||
"""Periodically ask the projector for its status"""
|
||||
while True:
|
||||
# Most queries only work when turned on, so if we're not sure, only query power
|
||||
if lastState[cmdMap["power"]] == "01":
|
||||
queries = cmdMap.values()
|
||||
else:
|
||||
queries = [cmdMap["power"]]
|
||||
|
||||
for val in queries:
|
||||
await barco_send(writer, f"[{val}?]")
|
||||
|
||||
await asyncio.sleep(2) # TODO find appropriate period
|
||||
|
||||
|
||||
async def main():
|
||||
global room, barcoReached, barcoPosition
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit("No position provided")
|
||||
else:
|
||||
barcoPosition = sys.argv[1]
|
||||
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
g62Barcos = {k: v for k,v in conf["barco_G62"].items()}
|
||||
currentBarco = g62Barcos[barcoPosition]
|
||||
barcoIP = currentBarco['ip']
|
||||
telnetPort = int(currentBarco["port"])
|
||||
|
||||
barcoReached = False
|
||||
try:
|
||||
barcoReader, barcoWriter = await telnetlib3.open_connection(barcoIP, telnetPort)
|
||||
barcoReached = True
|
||||
except Exception as e:
|
||||
print("Connection failed: " + barcoIP + ": " + str(e))
|
||||
barcoReached = False
|
||||
else:
|
||||
async with aiomqtt.Client(mqttHost, 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)
|
||||
await client.publish(f"{room}/projectors/{barcoPosition}/error", payload=("UNREACHABLE" if not barcoReached else "OK"))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
|
@ -47,12 +47,17 @@ async def shell(reader, writer):
|
|||
async with aiomqtt.Client('localhost', 1883) as client:
|
||||
task_status_query = asyncio.create_task(extron_audio_telnet_status(client, writer, reader))
|
||||
task_control = asyncio.create_task(extron_audio_telnet_control(client, writer))
|
||||
await asyncio.gather(task_control)
|
||||
await asyncio.gather(task_status_query, task_control)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
#coro = telnetlib3.open_connection('localhost', 1234, shell=shell)
|
||||
coro = telnetlib3.open_connection('192.168.192.14', 23, shell=shell)
|
||||
reader, writer = loop.run_until_complete(coro)
|
||||
loop.run_until_complete(writer.protocol.waiter_closed)
|
110
controller/lucke/luckeControl.py
Normal file
110
controller/lucke/luckeControl.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
from http import HTTPStatus
|
||||
|
||||
import aiomqtt
|
||||
import asyncio
|
||||
import toml
|
||||
import aiohttp
|
||||
import os
|
||||
|
||||
lucke_bearer_token: str
|
||||
room: str
|
||||
url: str
|
||||
roomId: int|str
|
||||
|
||||
async def sendSceneRequest(client, scene):
|
||||
endpoint = url + f"/rest/v2/fri-fkkt/lecture-halls/{roomId}/scenes/{scene}/activate"
|
||||
async with aiohttp.request("GET", endpoint, headers={"Authorization": "Bearer " + lucke_bearer_token}) as resp:
|
||||
if resp.status == 204:
|
||||
await client.publish(f'{room}/lucke/preset/current', payload=scene, qos=1, retain=True)
|
||||
else:
|
||||
print(resp.status, await resp.text())
|
||||
|
||||
|
||||
async def setLight(client, lightNum, intensity: int):
|
||||
endpoint = url + f"/rest/v2/fri-fkkt/lecture-halls/{roomId}/lights/{lightNum}"
|
||||
async with aiohttp.request("PUT", endpoint, headers={"Authorization": "Bearer " + lucke_bearer_token}, json={
|
||||
"stateOn": intensity != 0,
|
||||
"dimmValue": intensity
|
||||
}) as resp:
|
||||
if resp.status == 204:
|
||||
await client.publish(f'{room}/lucke/brightness/{lightNum}', payload=intensity, qos=1, retain=True)
|
||||
else:
|
||||
print("setLight error:", resp.status, await resp.text())
|
||||
|
||||
async def saveScene(client, sceneNum):
|
||||
endpoint = url + f"/rest/v2/fri-fkkt/lecture-halls/{roomId}/scenes/{sceneNum}/save"
|
||||
async with aiohttp.request("GET", endpoint, headers={"Authorization": "Bearer " + lucke_bearer_token}) as resp:
|
||||
if resp.status == 204:
|
||||
pass
|
||||
else:
|
||||
print("saveScene error:", resp.status, await resp.text())
|
||||
|
||||
async def task_luckeCommand(controlClient):
|
||||
await controlClient.subscribe(f"{room}/lucke/#")
|
||||
|
||||
async for mesg in controlClient.messages:
|
||||
mesg: aiomqtt.Message
|
||||
|
||||
if mesg.topic.matches(f"{room}/lucke/preset/recall"):
|
||||
sceneNum = mesg.payload.decode()
|
||||
print("Received: " + str(mesg.topic) + " payload: [" + sceneNum + "]")
|
||||
await sendSceneRequest(controlClient, sceneNum)
|
||||
|
||||
elif mesg.topic.matches(f"{room}/lucke/preset/save"):
|
||||
sceneNum = mesg.payload.decode()
|
||||
print("Received: " + str(mesg.topic) + " payload: [" + sceneNum + "]")
|
||||
await saveScene(controlClient, sceneNum)
|
||||
|
||||
elif mesg.topic.matches(f"{room}/lucke/set/+"):
|
||||
lightNum = mesg.topic.value.rsplit("/", maxsplit=1)[-1]
|
||||
try:
|
||||
intensity = int(mesg.payload.decode())
|
||||
assert 0 <= intensity <= 100
|
||||
except:
|
||||
print("Invalid message:", mesg)
|
||||
else:
|
||||
await setLight(controlClient, lightNum, intensity)
|
||||
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
async def task_luckePoll(client):
|
||||
"""Polls the API and sends light brightness to the API"""
|
||||
while True:
|
||||
endpoint = url + f"/rest/v2/fri-fkkt/lecture-halls/{roomId}/lights/"
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(endpoint, headers={"Authorization": f"Bearer {lucke_bearer_token}"}) as response:
|
||||
if response.status == 200:
|
||||
lights = await response.json()
|
||||
for light in lights:
|
||||
await client.publish(f'{room}/lucke/is_dimmable/{light["id"]}', payload=light["dimmable"], qos=1, retain=True)
|
||||
# TODO: find a better way to handle non-dimmable lights
|
||||
if light["stateOn"]:
|
||||
dimValue = light.get("dimmValue", 100)
|
||||
else:
|
||||
dimValue = 0
|
||||
await client.publish(f'{room}/lucke/brightness/{light["id"]}', payload=dimValue, qos=1, retain=True)
|
||||
|
||||
else:
|
||||
print(f"Failed to fetch lights: {response.status}", await response.text())
|
||||
await asyncio.sleep(.5)
|
||||
|
||||
async def main():
|
||||
global room, lucke_bearer_token, url, roomId
|
||||
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
url = conf["lucke"]['url']
|
||||
roomId = conf["lucke"]['roomId']
|
||||
lucke_bearer_token = conf["lucke"]['bearer_token']
|
||||
|
||||
async with aiomqtt.Client(mqttHost, mqttPort) as client:
|
||||
task_control = asyncio.create_task(task_luckeCommand(client))
|
||||
task_control = asyncio.create_task(task_luckePoll(client))
|
||||
await asyncio.gather(task_control)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
17
controller/malinaConfig.toml
Normal file
17
controller/malinaConfig.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[global]
|
||||
mqttHost = '192.168.192.42'
|
||||
mqttPort = 1883
|
||||
room = 'P01'
|
||||
|
||||
[lucke]
|
||||
url = 'http://192.168.190.90'
|
||||
roomId = 1
|
||||
bearer_token = '0954afe1-4111-4f89-a123-fea08a55dc46'
|
||||
|
||||
[barco_G62.main]
|
||||
ip = '192.168.192.12'
|
||||
port = 3023
|
||||
|
||||
[barco_G62.side]
|
||||
ip = '192.168.192.13'
|
||||
port = 3023
|
825
controller/poetry.lock
generated
Normal file
825
controller/poetry.lock
generated
Normal file
|
@ -0,0 +1,825 @@
|
|||
# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohappyeyeballs"
|
||||
version = "2.6.1"
|
||||
description = "Happy Eyeballs for asyncio"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"},
|
||||
{file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.12.13"
|
||||
description = "Async http client/server framework (asyncio)"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"},
|
||||
{file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"},
|
||||
{file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"},
|
||||
{file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"},
|
||||
{file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"},
|
||||
{file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"},
|
||||
{file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiohappyeyeballs = ">=2.5.0"
|
||||
aiosignal = ">=1.1.2"
|
||||
async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""}
|
||||
attrs = ">=17.3.0"
|
||||
frozenlist = ">=1.1.1"
|
||||
multidict = ">=4.5,<7.0"
|
||||
propcache = ">=0.2.0"
|
||||
yarl = ">=1.17.0,<2.0"
|
||||
|
||||
[package.extras]
|
||||
speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""]
|
||||
|
||||
[[package]]
|
||||
name = "aiomqtt"
|
||||
version = "2.3.0"
|
||||
description = "The idiomatic asyncio MQTT client, wrapped around paho-mqtt"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "aiomqtt-2.3.0-py3-none-any.whl", hash = "sha256:127926717bd6b012d1630f9087f24552eb9c4af58205bc2964f09d6e304f7e63"},
|
||||
{file = "aiomqtt-2.3.0.tar.gz", hash = "sha256:312feebe20bc76dc7c20916663011f3bd37aa6f42f9f687a19a1c58308d80d47"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
paho-mqtt = ">=2.1.0,<3.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "aioserial"
|
||||
version = "1.3.1"
|
||||
description = "An asynchronous serial port library of Python"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "aioserial-1.3.1.tar.gz", hash = "sha256:702bf03b0eb84b8ef2d8dac5cb925e1e685dce98f77b125569bc6fd2b3b58228"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyserial = "*"
|
||||
|
||||
[[package]]
|
||||
name = "aiosignal"
|
||||
version = "1.3.2"
|
||||
description = "aiosignal: a list of registered asynchronous callbacks"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"},
|
||||
{file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
frozenlist = ">=1.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "async-timeout"
|
||||
version = "5.0.1"
|
||||
description = "Timeout context manager for asyncio programs"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
|
||||
{file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "25.3.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
|
||||
{file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
||||
cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
||||
dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
||||
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
|
||||
tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
||||
tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
|
||||
|
||||
[[package]]
|
||||
name = "colorzero"
|
||||
version = "2.0"
|
||||
description = "Yet another Python color library"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "colorzero-2.0-py2.py3-none-any.whl", hash = "sha256:0e60d743a6b8071498a56465f7719c96a5e92928f858bab1be2a0d606c9aa0f8"},
|
||||
{file = "colorzero-2.0.tar.gz", hash = "sha256:e7d5a5c26cd0dc37b164ebefc609f388de24f8593b659191e12d85f8f9d5eb58"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
setuptools = "*"
|
||||
|
||||
[package.extras]
|
||||
doc = ["pkginfo", "sphinx", "sphinx-rtd-theme"]
|
||||
test = ["pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.7.0"
|
||||
description = "A list-like structure which implements collections.abc.MutableSequence"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e"},
|
||||
{file = "frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7"},
|
||||
{file = "frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3"},
|
||||
{file = "frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81"},
|
||||
{file = "frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e"},
|
||||
{file = "frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63"},
|
||||
{file = "frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e"},
|
||||
{file = "frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpiozero"
|
||||
version = "2.0.1"
|
||||
description = "A simple interface to GPIO devices with Raspberry Pi"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "gpiozero-2.0.1-py3-none-any.whl", hash = "sha256:8f621de357171d574c0b7ea0e358cb66e560818a47b0eeedf41ce1cdbd20c70b"},
|
||||
{file = "gpiozero-2.0.1.tar.gz", hash = "sha256:d4ea1952689ec7e331f9d4ebc9adb15f1d01c2c9dcfabb72e752c9869ab7e97e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorzero = "*"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx (>=4.0)", "sphinx-rtd-theme (>=1.0)"]
|
||||
test = ["pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
|
||||
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.6.4"
|
||||
description = "multidict implementation"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0"},
|
||||
{file = "multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e"},
|
||||
{file = "multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e"},
|
||||
{file = "multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf"},
|
||||
{file = "multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4"},
|
||||
{file = "multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:af7618b591bae552b40dbb6f93f5518328a949dac626ee75927bba1ecdeea9f4"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b6819f83aef06f560cb15482d619d0e623ce9bf155115150a85ab11b8342a665"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4d09384e75788861e046330308e7af54dd306aaf20eb760eb1d0de26b2bea2cb"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a59c63061f1a07b861c004e53869eb1211ffd1a4acbca330e3322efa6dd02978"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350f6b0fe1ced61e778037fdc7613f4051c8baf64b1ee19371b42a3acdb016a0"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c5cbac6b55ad69cb6aa17ee9343dfbba903118fd530348c330211dc7aa756d1"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:630f70c32b8066ddfd920350bc236225814ad94dfa493fe1910ee17fe4365cbb"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8d4916a81697faec6cb724a273bd5457e4c6c43d82b29f9dc02c5542fd21fc9"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e42332cf8276bb7645d310cdecca93a16920256a5b01bebf747365f86a1675b"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f3be27440f7644ab9a13a6fc86f09cdd90b347c3c5e30c6d6d860de822d7cb53"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:21f216669109e02ef3e2415ede07f4f8987f00de8cdfa0cc0b3440d42534f9f0"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d9890d68c45d1aeac5178ded1d1cccf3bc8d7accf1f976f79bf63099fb16e4bd"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:edfdcae97cdc5d1a89477c436b61f472c4d40971774ac4729c613b4b133163cb"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0b2e886624be5773e69cf32bcb8534aecdeb38943520b240fed3d5596a430f2f"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:be5bf4b3224948032a845d12ab0f69f208293742df96dc14c4ff9b09e508fc17"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-win32.whl", hash = "sha256:10a68a9191f284fe9d501fef4efe93226e74df92ce7a24e301371293bd4918ae"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:ee25f82f53262f9ac93bd7e58e47ea1bdcc3393cef815847e397cba17e284210"},
|
||||
{file = "multidict-6.6.4-cp39-cp39-win_arm64.whl", hash = "sha256:f9867e55590e0855bcec60d4f9a092b69476db64573c9fe17e92b0c50614c16a"},
|
||||
{file = "multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c"},
|
||||
{file = "multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "paho-mqtt"
|
||||
version = "2.1.0"
|
||||
description = "MQTT version 5.0/3.1.1 client class"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee"},
|
||||
{file = "paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
proxy = ["pysocks"]
|
||||
|
||||
[[package]]
|
||||
name = "propcache"
|
||||
version = "0.3.2"
|
||||
description = "Accelerated property cache"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70"},
|
||||
{file = "propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897"},
|
||||
{file = "propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1"},
|
||||
{file = "propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02"},
|
||||
{file = "propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394"},
|
||||
{file = "propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1"},
|
||||
{file = "propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9"},
|
||||
{file = "propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f"},
|
||||
{file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyserial"
|
||||
version = "3.5"
|
||||
description = "Python Serial Port Extension"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"},
|
||||
{file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cp2110 = ["hidapi"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.6.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"},
|
||||
{file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.7.0) ; sys_platform != \"cygwin\""]
|
||||
core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"]
|
||||
cover = ["pytest-cov"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
||||
enabler = ["pytest-enabler (>=2.2)"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
|
||||
type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (>=1.12,<1.14)", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "smbus2"
|
||||
version = "0.5.0"
|
||||
description = "smbus2 is a drop-in replacement for smbus-cffi/smbus-python in pure Python"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "smbus2-0.5.0-py2.py3-none-any.whl", hash = "sha256:1a15c3b9fa69357beb038cc0b5d37939702f8bfde1ddc89ca9f17d8461dbe949"},
|
||||
{file = "smbus2-0.5.0.tar.gz", hash = "sha256:4a5946fd82277870c2878befdb1a29bb28d15cda14ea4d8d2d54cf3d4bdcb035"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=1.5.3)"]
|
||||
qa = ["flake8"]
|
||||
|
||||
[[package]]
|
||||
name = "telnetlib3"
|
||||
version = "2.0.4"
|
||||
description = "Python 3 asyncio Telnet server and client Protocol library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "telnetlib3-2.0.4-py2.py3-none-any.whl", hash = "sha256:b3c0f984a7fb1b6ee16e6fdaa410c56389b0dc492174a99c6661b1ba4c9d457d"},
|
||||
{file = "telnetlib3-2.0.4.tar.gz", hash = "sha256:dbcbc16456a0e03a62431be7cfefff00515ab2f4ce2afbaf0d3a0e51a98c948d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.14.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.9+"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"},
|
||||
{file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.20.1"
|
||||
description = "Yet another URL library"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8"},
|
||||
{file = "yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773"},
|
||||
{file = "yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5"},
|
||||
{file = "yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7"},
|
||||
{file = "yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d"},
|
||||
{file = "yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06"},
|
||||
{file = "yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00"},
|
||||
{file = "yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77"},
|
||||
{file = "yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
idna = ">=2.0"
|
||||
multidict = ">=4.0"
|
||||
propcache = ">=0.2.1"
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "b0cd468209f60a9f821063be3900cc54677b0e93b7959a05971c1594d9d8c06d"
|
147
controller/projector_motors/projector_motors.py
Normal file
147
controller/projector_motors/projector_motors.py
Normal file
|
@ -0,0 +1,147 @@
|
|||
MOCK = False
|
||||
import aiomqtt
|
||||
import asyncio
|
||||
from smbus2 import SMBus
|
||||
import toml
|
||||
import os
|
||||
|
||||
room: str
|
||||
use_offset = False
|
||||
|
||||
""" 0 1 2 3 """
|
||||
relayMasks = [0b0001, 0b0010, 0b0100, 0b1000] #probably ne rabim
|
||||
|
||||
bus = SMBus(1)
|
||||
|
||||
i2c_map = {
|
||||
'main': -1,
|
||||
'side': -1,
|
||||
}
|
||||
|
||||
relMapping = {
|
||||
'service_down': 0,
|
||||
'service_up': 1,
|
||||
'down': 2,
|
||||
'up': 3
|
||||
}
|
||||
|
||||
currentState = {
|
||||
'main': 0b0000,
|
||||
'side': 0b0000
|
||||
}
|
||||
|
||||
async def msgRelayBoard(projSelect, command, state: bool):
|
||||
# Select the correct relay board
|
||||
i2c_addr = i2c_map[projSelect]
|
||||
|
||||
# Get the relay position for the given command
|
||||
maskShift = relMapping[command]
|
||||
# Set the corresponding bit
|
||||
mask = (1 << maskShift)
|
||||
if state:
|
||||
currentState[projSelect] = currentState[projSelect] | mask
|
||||
else:
|
||||
currentState[projSelect] = currentState[projSelect] & ~mask
|
||||
|
||||
# Write it to the I2C bus (0x10 is the register for relay states)
|
||||
bus.write_byte_data(i2c_addr, 0x10, currentState[projSelect])
|
||||
print(projSelect, "{:04b}".format(currentState[projSelect]))
|
||||
|
||||
|
||||
"""
|
||||
SrvDwn SrvUp OpDwn OpUp
|
||||
MAIN: 0x42 0001 0010 0100 1000
|
||||
SIDE: 0x43 0001 0010 0100 1000
|
||||
"""
|
||||
|
||||
|
||||
#old board
|
||||
"""
|
||||
MAIN: SrvDwn SrvUp OpDwn OpUp
|
||||
0 1 2 3
|
||||
SIDE: 4 5 6 7
|
||||
"""
|
||||
#dej like bolš to podukumentiraj or smth
|
||||
|
||||
async def task_command2relays(controlClient: aiomqtt.Client):
|
||||
"""Read commands from MQTT and send them to the relays"""
|
||||
|
||||
await controlClient.subscribe(f"{room}/projectors/+/lift/#")
|
||||
|
||||
async for mesg in controlClient.messages:
|
||||
msgTopicArr = mesg.topic.value.split('/')
|
||||
value = mesg.payload.decode()
|
||||
|
||||
if mesg.topic.matches(f'{room}/projectors/+/lift/manual/+'):
|
||||
command = msgTopicArr[-1]
|
||||
projSel = msgTopicArr[-4]
|
||||
|
||||
if projSel not in ("main", "side") or command not in relMapping.keys() or value not in ("0", "1"):
|
||||
print("Invalid manual command:", projSel, command, value)
|
||||
continue
|
||||
|
||||
await msgRelayBoard(projSel, command, value == '1')
|
||||
|
||||
# Service move
|
||||
if "service" in command:
|
||||
# Manual control makes the position unknown, so we clear it
|
||||
if value == "1":
|
||||
status = "MOVING"
|
||||
else:
|
||||
status = "STOPPED"
|
||||
await controlClient.publish(f'{room}/projectors/{projSel}/lift/status', payload=status, qos=1, retain=True)
|
||||
# Normal move
|
||||
else:
|
||||
# HACK: if the press is too short it doesn't register, so we sleep for a bit
|
||||
if value == "1":
|
||||
await asyncio.sleep(.2)
|
||||
|
||||
#print("Pushing \'off\' to other relays to prevent conflicts")
|
||||
|
||||
elif mesg.topic.matches(f'{room}/projectors/+/lift/goto'):
|
||||
projSel = msgTopicArr[-3]
|
||||
|
||||
if projSel not in ("main", "side") or value not in ("UP", "DOWN"):
|
||||
print("Invalid goto command:", projSel, value)
|
||||
continue
|
||||
|
||||
# Clear manual control
|
||||
await msgRelayBoard(projSel, "service_down", False)
|
||||
await msgRelayBoard(projSel, "service_up", False)
|
||||
|
||||
if value == "UP":
|
||||
other = "down"
|
||||
elif value == "DOWN":
|
||||
other = "up"
|
||||
await msgRelayBoard(projSel, other, False)
|
||||
|
||||
# Click the buttom for a bit and release it, then publish that the lift has moved
|
||||
await msgRelayBoard(projSel, value.lower(), True)
|
||||
await asyncio.sleep(1)
|
||||
await msgRelayBoard(projSel, value.lower(), False)
|
||||
|
||||
await controlClient.publish(f'{room}/projectors/{projSel}/lift/status', payload=value, qos=1, retain=True)
|
||||
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
|
||||
|
||||
async def main():
|
||||
global i2c_map, room
|
||||
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
projMotors = conf["projector_motors"]
|
||||
i2c_map['main'] = projMotors["main"]['i2c_address']
|
||||
i2c_map['side'] = projMotors["side"]['i2c_address']
|
||||
|
||||
async with aiomqtt.Client(mqttHost, mqttPort) as client:
|
||||
task_control = asyncio.create_task(task_command2relays(client))
|
||||
await asyncio.gather(task_control)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
|
@ -15,6 +15,8 @@ gpiozero = "^2.0.1"
|
|||
telnetlib3 = "^2.0.4"
|
||||
toml = "^0.10.2"
|
||||
smbus2 = "^0.5.0"
|
||||
aiohttp = "^3.12.13"
|
||||
multidict = "^6.6.4"
|
||||
|
||||
|
||||
[build-system]
|
251
controller/tse_serial/tse_serial_controler.py
Normal file
251
controller/tse_serial/tse_serial_controler.py
Normal file
|
@ -0,0 +1,251 @@
|
|||
import asyncio
|
||||
import serial
|
||||
import aioserial
|
||||
import aiomqtt
|
||||
from tse_serial_interpreter import *
|
||||
from dataclasses import dataclass
|
||||
import os
|
||||
import sys
|
||||
import toml
|
||||
import serial.tools.list_ports
|
||||
|
||||
# GLOBALS
|
||||
room: str
|
||||
platnoBckgdMoving = {
|
||||
'main': False,
|
||||
'side': False,
|
||||
}
|
||||
|
||||
# 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()
|
||||
candidates = ('/dev/ttyUSB0', '/dev/ttyACM0', '/dev/ttyUSB1', '/dev/ttyACM1')
|
||||
serport = ''
|
||||
# Find first serial port that exists
|
||||
for port in portList:
|
||||
if port.device in candidates:
|
||||
serport = port.device
|
||||
break
|
||||
else:
|
||||
print(portList)
|
||||
sys.exit("No serial port found")
|
||||
|
||||
aser: aioserial.AioSerial = aioserial.AioSerial(
|
||||
port=serport,
|
||||
baudrate=1200,
|
||||
parity=serial.PARITY_NONE,
|
||||
bytesize=serial.EIGHTBITS,
|
||||
stopbits=serial.STOPBITS_ONE
|
||||
)
|
||||
|
||||
# altho itak je ta script za tist specific tse box in so vsi isti
|
||||
mapping_toggles = {
|
||||
"master": 1,
|
||||
"audio": 2,
|
||||
"projectors": 3,
|
||||
}
|
||||
# 4 is not connected to anything
|
||||
platno_mapping = {
|
||||
"main": {
|
||||
"DOWN": 5,
|
||||
"UP": 6
|
||||
},
|
||||
"side": {
|
||||
"DOWN": 7,
|
||||
"UP": 8
|
||||
}
|
||||
}
|
||||
|
||||
shades_mapping = {
|
||||
"DOWN": 9,
|
||||
"UP": 10
|
||||
}
|
||||
|
||||
reverse_lookup = {
|
||||
1: ("power", "master", ""),
|
||||
2: ("power", "audio", ""),
|
||||
3: ("power", "projectors", ""),
|
||||
|
||||
5: ("platno", "main", "MOVING_DOWN"),
|
||||
6: ("platno", "main", "MOVING_UP"),
|
||||
|
||||
7: ("platno", "side", "MOVING_DOWN"),
|
||||
8: ("platno", "side", "MOVING_UP"),
|
||||
|
||||
9: ("shades", "MOVING_DOWN"),
|
||||
10: ("shades", "MOVING_UP")
|
||||
}
|
||||
|
||||
#TODO finish this
|
||||
#TODO add doc comment to every task funciton
|
||||
async def task_status2mqtt(statusClient: aiomqtt.Client):
|
||||
while True:
|
||||
data = await aser.read_until_async()
|
||||
data = data.decode(errors='ignore').strip()
|
||||
print("TSE box sent: " + data)
|
||||
relState = resp_to_relay_state(data)
|
||||
if relState.relay_id is None:
|
||||
continue # TODO handling - nebi smelo do tega prit anyway
|
||||
publishTopic = f"{room}/"
|
||||
publishPayload = ""
|
||||
lookup = reverse_lookup[relState.relay_id]
|
||||
if lookup[0] == "power":
|
||||
publishTopic += f"{lookup[0]}/{lookup[1]}/status"
|
||||
publishPayload = '1' if relState.state else '0'
|
||||
elif lookup[0] == "shades":
|
||||
publishTopic += f"{lookup[0]}/status"
|
||||
publishPayload = 'STOPPED' if not relState.state else lookup[1]
|
||||
elif lookup[0] == "platno":
|
||||
publishTopic += f"projectors/{lookup[1]}/{lookup[0]}/status"
|
||||
publishPayload = 'UNKNOWN' if not relState.state else lookup[2]
|
||||
#publishTopic = f"{room}/projectors/{}"
|
||||
#publishPayload = "1" if relState.state else "0"
|
||||
print("Publishing [" + publishPayload + "] to topic [" + publishTopic + "]")
|
||||
await statusClient.publish(publishTopic, payload=publishPayload)
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
|
||||
async def executeAndPublish(mqttClient, pubTopic, pubPayload, relStat):
|
||||
setRelayCmd = relay_state_to_cmd(relStat)
|
||||
#print("Received: [" + topicVal + "] payload: [" + pubPayload + "]")
|
||||
print("Sending to TSE box: " + setRelayCmd)
|
||||
print(f"Also publishing topic [{pubTopic}] with status [{pubPayload}]")
|
||||
print()
|
||||
await aser.write_async(bytes(setRelayCmd + '\r\n', "ascii"))
|
||||
await mqttClient.publish(pubTopic, payload=pubPayload)
|
||||
await asyncio.sleep(0.1) #TODO probably remove
|
||||
|
||||
|
||||
async def handleTsePower(client, sysSelect, cmd):
|
||||
rel = RelayState(mapping_toggles[sysSelect], cmd == '1')
|
||||
await executeAndPublish(client, f'{room}/power/{sysSelect}/status', cmd, rel)
|
||||
|
||||
async def handleTseSencilo(client, cmd):
|
||||
#relName = tval.split('/')[3]
|
||||
topicPub = f'{room}/shades/status'
|
||||
if cmd == "MOVE_UP":
|
||||
rel = RelayState(shades_mapping['UP'], True)
|
||||
await executeAndPublish(client, topicPub, "MOVING_UP", rel)
|
||||
elif cmd == "MOVE_DOWN":
|
||||
rel = RelayState(shades_mapping['DOWN'], True)
|
||||
await executeAndPublish(client, topicPub, "MOVING_DOWN", rel)
|
||||
else:
|
||||
await executeAndPublish(client, topicPub, "STOPPED", RelayState(shades_mapping['UP'], False))
|
||||
await executeAndPublish(client, topicPub, "STOPPED", RelayState(shades_mapping['DOWN'], False))
|
||||
|
||||
|
||||
async def platnoTimeout(mqttClient, pubTopic, pubPayload, relStat: RelayState, intent, select):
|
||||
global platnoBckgdMoving
|
||||
await asyncio.sleep(25) #TODO time actual delay
|
||||
relStat.state = False
|
||||
await executeAndPublish(mqttClient, pubTopic, intent, relStat)
|
||||
platnoBckgdMoving[select] = False #TODO properly document why this is here and what it does
|
||||
|
||||
|
||||
async def handleTsePlatno(client, proj, cmdType, cmd):
|
||||
global platnoBckgdMoving
|
||||
|
||||
pubTop = f'{room}/projectors/{proj}/platno/status'
|
||||
if not (proj == "main" or proj == "side"):
|
||||
return
|
||||
if platnoBckgdMoving[proj]:
|
||||
if cmd == 'STOP':
|
||||
pubPld = 'UNKNOWN'
|
||||
rel1 = RelayState(platno_mapping[proj]['UP'], False)
|
||||
rel2 = RelayState(platno_mapping[proj]['DOWN'], False)
|
||||
await executeAndPublish(client,pubTop, pubPld, rel1)
|
||||
await executeAndPublish(client,pubTop, pubPld, rel2)
|
||||
platnoBckgdMoving[proj] = False
|
||||
else:
|
||||
print("Ignored command because already moving", proj, cmdType, cmd)
|
||||
return
|
||||
|
||||
if cmdType == 'move':
|
||||
print('move')
|
||||
rel: RelayState
|
||||
if cmd == 'UP':
|
||||
rel = RelayState(platno_mapping[proj]['UP'], True)
|
||||
pubPld = 'MOVING_UP'
|
||||
elif cmd == 'DOWN':
|
||||
rel = RelayState(platno_mapping[proj]['DOWN'], True)
|
||||
pubPld = 'MOVING_DOWN'
|
||||
else:
|
||||
return # in case of invalid input skip
|
||||
platnoBckgdMoving[proj] = True #TODO rename to moving, add comment how it works
|
||||
await executeAndPublish(client, pubTop, pubPld, rel)
|
||||
#TODO WTF HAPPENS IF YOU SEND UP AND DOWN AT ONCE?? (screenshot?)
|
||||
#TODO daj ignore print ko je locked up
|
||||
|
||||
elif cmdType == 'goto':
|
||||
print('received GOTO')
|
||||
rel: RelayState
|
||||
if cmd == 'UP':
|
||||
rel = RelayState(platno_mapping[proj]['UP'], True)
|
||||
intent = 'UP'
|
||||
pubPld = 'MOVING_UP'
|
||||
elif cmd == 'DOWN':
|
||||
rel = RelayState(platno_mapping[proj]['DOWN'], True)
|
||||
intent = 'DOWN'
|
||||
pubPld = 'MOVING_DOWN'
|
||||
else:
|
||||
return # in case of invalid input skip
|
||||
platnoBckgdMoving[proj] = True
|
||||
# pubPld = 'MOVING'
|
||||
await executeAndPublish(client, pubTop, pubPld, rel)
|
||||
asyncio.create_task(platnoTimeout(client, pubTop, pubPld, rel, intent, proj))
|
||||
|
||||
else:
|
||||
print('unknown command')
|
||||
|
||||
async def task_command2serial(controlClient: aiomqtt.Client):
|
||||
await controlClient.subscribe(f"{room}/#")
|
||||
|
||||
async for mesg in controlClient.messages:
|
||||
topicVal = mesg.topic.value
|
||||
msgTopic = mesg.topic.value.split('/')
|
||||
cmnd = mesg.payload.decode()
|
||||
|
||||
if mesg.topic.matches(f'{room}/projectors/+/platno/move') or mesg.topic.matches(f'{room}/projectors/+/platno/goto'):
|
||||
proj = msgTopic[-3]
|
||||
cmdType = msgTopic[-1] # move / goto
|
||||
print(f'on {topicVal} received: {cmnd}')
|
||||
await handleTsePlatno(controlClient, proj, cmdType, cmnd) #TODO odzadi index
|
||||
|
||||
elif mesg.topic.matches(f'{room}/power/+/set'):
|
||||
systype = msgTopic[-2]
|
||||
print(f'on {topicVal} received: {cmnd}')
|
||||
print('calling power')
|
||||
await handleTsePower(controlClient, systype, cmnd)
|
||||
|
||||
elif mesg.topic.matches(f'{room}/shades/move'):
|
||||
print(f'on {topicVal} received: {cmnd}')
|
||||
print('calling move')
|
||||
await handleTseSencilo(controlClient, cmnd)
|
||||
|
||||
else:
|
||||
continue
|
||||
# code after if block doesnt execute in this case
|
||||
#print("after")
|
||||
await asyncio.sleep(0.01) #TODO do we need this? (probably)
|
||||
|
||||
|
||||
async def main():
|
||||
global room
|
||||
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
async with aiomqtt.Client(mqttHost, mqttPort) as client:
|
||||
task_status = asyncio.create_task(task_status2mqtt(client))
|
||||
task_control = asyncio.create_task(task_command2serial(client))
|
||||
await asyncio.gather(task_status, task_control)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
15
docs/Lučke.md
Normal file
15
docs/Lučke.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
Lučke v velikih predavalnicah
|
||||
===
|
||||
|
||||
## Mapping po predavalnicah
|
||||
|
||||
### P01
|
||||
|
||||
- 1 - Neonke tabla
|
||||
- 2 - Neonke sredina
|
||||
- 3 - Neonke vrata
|
||||
- 4 - Okrogle 2 (vse razen ene stropne luči zgoraj)
|
||||
- 5 - Neonke začetek
|
||||
- 6 - Reflektorji 2 (ne dela)
|
||||
- 7 - Okrogle 1 (ena stropna luč zgoraj)
|
||||
- 8 - Stopnice
|
|
@ -1,5 +1,6 @@
|
|||
Prostor,Naprava,Model,IPv4,Naslov
|
||||
-,Multimedija server,,192.168.192.9,multimedija.fri1.uni-lj.si
|
||||
P01,Malinca,Raspberry Pi 5,192.168.192.42,
|
||||
P01,Crestron krmilnik,Crestron AV3,192.168.192.10,
|
||||
P01,Crestron tablica,Crestron TSW550,192.168.192.11,
|
||||
P01,Glavni projektor,Barco G62,192.168.192.12,
|
||||
|
|
|
|
@ -1,8 +0,0 @@
|
|||
[Match]
|
||||
|
||||
Name=eth0
|
||||
|
||||
[Network]
|
||||
Address=192.168.192.42
|
||||
Gateway=192.168.192.1
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
[Unit]
|
||||
Description=Extron audio matrix control
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/python3 /home/rpi/extron_audio_matrix_telnet_control.py
|
||||
Type=simple
|
||||
Restart=always
|
||||
User=kat
|
||||
Group=kat
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
17
frontend/README.md
Normal file
17
frontend/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
<title>MMM krmilnik</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
114
frontend/src/App.vue
Normal file
114
frontend/src/App.vue
Normal file
|
@ -0,0 +1,114 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, onMounted, watch} from 'vue'
|
||||
import MainPage from './components/pages/MainPage.vue';
|
||||
import VideoPage from './components/pages/VideoPage.vue';
|
||||
import VerticalTabs from './components/tabs/VerticalTabs.vue';
|
||||
import Tab from './components/tabs/Tab.vue';
|
||||
import LightingPage from './components/pages/LightingPage.vue';
|
||||
import ServisPage from './components/pages/ServisPage.vue';
|
||||
import {$mqtt} from "vue-paho-mqtt"
|
||||
import AudioPage from "@/components/pages/AudioPage.vue";
|
||||
|
||||
|
||||
document.addEventListener('contextmenu', event => event.preventDefault());
|
||||
|
||||
|
||||
let urlParams = new URLSearchParams(window.location.search);
|
||||
const currentRoom = ref(urlParams.get('room') || 'none')
|
||||
|
||||
const pageNum = ref(0)
|
||||
|
||||
const mqttStat = ref($mqtt.status())
|
||||
|
||||
watch(mqttStat, (_, newState) => {
|
||||
|
||||
})
|
||||
|
||||
const servisActuve = ref(false)
|
||||
|
||||
|
||||
watch(pageNum, (_, newState) => {
|
||||
console.log(pageNum)
|
||||
// console.log(newState)
|
||||
servisActuve.value = (pageNum.value == 4);
|
||||
})
|
||||
|
||||
//TODO display none namest uno
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="currentRoom == 'none'">
|
||||
<h1>Missing room parameter! Add e.g. <code>?room=P01</code> to the URL</h1>
|
||||
</div>
|
||||
<div v-else id="wrapper">
|
||||
<header class="sidebar">
|
||||
<img class="logo" src="https://fri.uni-lj.si/sites/all/themes/fri_theme/images/fri_logo.png"/>
|
||||
<h1>{{ currentRoom.toUpperCase() }}</h1>
|
||||
|
||||
<VerticalTabs id="nav">
|
||||
<Tab @click="pageNum=0" :selected="pageNum==0">Priprava</Tab>
|
||||
<Tab @click="pageNum=1" :selected="pageNum==1">Video</Tab>
|
||||
<Tab @click="pageNum=2" :selected="pageNum==2">Audio</Tab>
|
||||
<Tab @click="pageNum=3" :selected="pageNum==3">Lučke</Tab>
|
||||
<Tab @click="pageNum=4" :selected="pageNum==4">Servis</Tab>
|
||||
</VerticalTabs>
|
||||
<div class="mstatus" v-if="$mqtt.status() != 'connected'">{{ $mqtt.status()?.toUpperCase() }}</div>
|
||||
<button class="reload" v-if="$mqtt.status() != 'connected'" onclick="window.location.reload()">RELOAD</button>
|
||||
|
||||
</header>
|
||||
<main>
|
||||
<MainPage :class="{'hiddenPage': pageNum != 0}" :room="currentRoom"/>
|
||||
<VideoPage :class="{'hiddenPage': pageNum != 1}" :room="currentRoom"/>
|
||||
<AudioPage :class="{'hiddenPage': pageNum != 2}" :room="currentRoom"/>
|
||||
<LightingPage :class="{'hiddenPage': pageNum != 3}" :room="currentRoom"/>
|
||||
<ServisPage :class="{'hiddenPage': pageNum != 4}" :room="currentRoom" :currently-active="servisActuve"/>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.reload {
|
||||
opacity: .8;
|
||||
font-size: .8em;
|
||||
margin: 0 3em;
|
||||
}
|
||||
|
||||
.hiddenPage {
|
||||
display: none !important
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
main > * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
max-width: 10em;
|
||||
}
|
||||
|
||||
.logo {
|
||||
max-width: 100%;
|
||||
padding-left: .8rem;
|
||||
padding-top: .8rem;
|
||||
}
|
||||
|
||||
.mstatus {
|
||||
text-align: center;
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
113
frontend/src/assets/main.css
Normal file
113
frontend/src/assets/main.css
Normal file
|
@ -0,0 +1,113 @@
|
|||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-text: #000;
|
||||
--color-background: #EEE;
|
||||
|
||||
--color-brand-ul-red: #e03127;
|
||||
--color-brand-ul-light-grey: #E6E7E8;
|
||||
--color-brand-ul-medium-grey: #A7A8AA;
|
||||
--color-brand-ul-dark-grey: #58595b;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family: "Noto Sans", sans-serif;
|
||||
font-size: 17px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
|
||||
* {
|
||||
user-select: none;
|
||||
}
|
||||
html, body {
|
||||
touch-action: manipulation;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
font-size: 1.5em;
|
||||
color: #58595b;
|
||||
}
|
||||
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.mstatus {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
button {
|
||||
--bg-default: #ffffff;
|
||||
--bg-active: lightgray;
|
||||
background: var(--bg-default);
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: .1rem;
|
||||
border: 1px solid #000000;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
/* The last touched button keeps focus, so we shoudln't highlight that */
|
||||
button:focus:not(:active) {
|
||||
background: var(--bg-default);
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: var(--bg-active);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
h3 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.currentlyActive {
|
||||
background-color: orange !important
|
||||
}
|
85
frontend/src/components/AudioControlModule.vue
Normal file
85
frontend/src/components/AudioControlModule.vue
Normal file
|
@ -0,0 +1,85 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import { ref, onMounted, reactive, watch } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
big: [Boolean, null]
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/power/audio/status',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const audioStatus = ref(false)
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
audioStatus.value = msg == '1'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
async function setAudio() {
|
||||
let topicPref = props.room + "/power/audio/set"
|
||||
let command = '0'
|
||||
if (!audioStatus.value) {
|
||||
command = '1'
|
||||
}
|
||||
publishMQTTMsg(topicPref, command)
|
||||
//audioStatus.value = command == '1'
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
|
||||
const roomState = ref(0)
|
||||
// OFF -> 0; ON -> 1; IN BETWEEN -> 2
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Ozvočenje</h3>
|
||||
<button @click="setAudio()" :class="{big:big}">
|
||||
{{ audioStatus ? 'IZKLOP' : 'VKLOP' }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
.big {
|
||||
font-size: 1.8rem;
|
||||
height: 5em;
|
||||
}
|
||||
</style>
|
128
frontend/src/components/LectureModule.vue
Normal file
128
frontend/src/components/LectureModule.vue
Normal file
|
@ -0,0 +1,128 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, onMounted, reactive, watch} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import ProjectorShutter from './ProjectorShutter.vue'
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String,
|
||||
})
|
||||
|
||||
const status = reactive({
|
||||
power: '?',
|
||||
platno: '?',
|
||||
lift: '?',
|
||||
wait: false,
|
||||
})
|
||||
|
||||
$mqtt.subscribe(props.room + '/projectors/' + props.position + '/status/power', (message) => {
|
||||
console.debug("a")
|
||||
status.power = message;
|
||||
});
|
||||
$mqtt.subscribe(props.room + '/projectors/' + props.position + '/platno/status', (message) => {
|
||||
status.platno = message;
|
||||
});
|
||||
$mqtt.subscribe(props.room + '/projectors/' + props.position + '/lift/status', (message) => {
|
||||
status.lift = message;
|
||||
});
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, ms)
|
||||
})
|
||||
}
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Fnr')
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
async function startLecture() {
|
||||
publishMQTTMsg(props.room + '/power/master/set', '1')
|
||||
publishMQTTMsg(props.room + '/power/audio/set', '1')
|
||||
publishMQTTMsg(props.room + '/power/projectors/set', '1')
|
||||
await sleep(500)
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/set/power', '1')
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/platno/goto', "DOWN")
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/lift/goto', 'DOWN')
|
||||
|
||||
status.wait = true
|
||||
setTimeout(() => {
|
||||
status.wait = false
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
async function stopLecture() {
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/set/power', '0')
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/platno/goto', "UP")
|
||||
// publishMQTTMsg(props.room + '/projectors/' + props.position + 'lift/goto', 'DOWN')
|
||||
status.wait = true
|
||||
setTimeout(() => {
|
||||
status.wait = false
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<small class="status">
|
||||
PROJ: {{ status.power }}
|
||||
PLAT: {{ status.platno }}
|
||||
LIFT: {{ status.lift }}
|
||||
</small>
|
||||
|
||||
<button class="currentlyActive" @click="stopLecture()" v-if="status.power == '1' || status.platno == 'DOWN'" :class="{waiting: status.wait}">
|
||||
IZKLOP
|
||||
</button>
|
||||
<button @click="startLecture()" v-else="" :class="{waiting: status.wait}">
|
||||
VKLOP
|
||||
</button>
|
||||
|
||||
<ProjectorShutter :room="props.room" :position="props.position"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.status {
|
||||
font-size: .8em;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
|
||||
height: 9rem;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.wait {
|
||||
animation: fade-in-out 1s infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes fade-in-out {
|
||||
0% {
|
||||
opacity: .2;
|
||||
}
|
||||
50% {
|
||||
opacity: .8;
|
||||
}
|
||||
100% {
|
||||
opacity: .2;
|
||||
}
|
||||
}
|
||||
</style>
|
104
frontend/src/components/Lift.vue
Normal file
104
frontend/src/components/Lift.vue
Normal file
|
@ -0,0 +1,104 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import DownIcon from './icons/DownIcon.vue';
|
||||
import UpIcon from './icons/UpIcon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String
|
||||
})
|
||||
|
||||
const topicPrefix = `${props.room}/projectors/${props.position}`
|
||||
|
||||
const status = reactive({
|
||||
status: "-",
|
||||
})
|
||||
|
||||
$mqtt.subscribe(`${topicPrefix}/lift/status`, (message) => {
|
||||
status.status = message;
|
||||
});
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Fnr')
|
||||
}
|
||||
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/lift/')
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<!--
|
||||
TODO: NE HARDCODANO
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div style="display:flex; gap: 1rem">
|
||||
<div>
|
||||
<h4>Lifti ({{status.status}})</h4>
|
||||
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/up', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/up', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/up', '0')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/down', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/down', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/down', '0')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Servis</h4>
|
||||
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/service_up', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/service_up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/service_up', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/service_up', '0')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/service_down', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/service_down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/service_down', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/service_down', '0')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h4>GOTO</h4>
|
||||
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'UP')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'DOWN')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 0.1rem;
|
||||
width: 45%;
|
||||
}
|
||||
</style>
|
122
frontend/src/components/LightControl.vue
Normal file
122
frontend/src/components/LightControl.vue
Normal file
|
@ -0,0 +1,122 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive } from "vue";
|
||||
import { $mqtt } from "vue-paho-mqtt";
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
id: Number,
|
||||
name: String,
|
||||
dimmable: Boolean,
|
||||
});
|
||||
|
||||
const topicPrefix = `${props.room}/lucke`
|
||||
|
||||
const status = reactive({
|
||||
brightness: 0,
|
||||
})
|
||||
|
||||
$mqtt.subscribe(`${topicPrefix}/brightness/${props.id}`, (message) => {
|
||||
status.brightness = parseInt(message);
|
||||
});
|
||||
|
||||
function doBrightness(brightness: Number) {
|
||||
$mqtt.publish(`${topicPrefix}/set/${props.id}`, brightness.toFixed(0), 'Fnr');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="props.dimmable">
|
||||
<label>{{props.name}}</label>
|
||||
<input type="range" min="0" max="100" step="10" :value="status.brightness" @input="doBrightness(parseInt(($event.target as HTMLInputElement).value))" >
|
||||
</div>
|
||||
<span v-else>
|
||||
<label>{{props.name}}</label>
|
||||
<button @click="doBrightness(status.brightness != 0 ? 0 : 100)" :class="{currentlyActive: status.brightness != 0 }">
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
border: 1px solid #000000;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-flex
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: .5rem
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
flex: 1;
|
||||
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
||||
--width: 100%; /* Specific width is required for Firefox. */
|
||||
background: transparent; /* Otherwise white in Chrome */
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 8.4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
background: lightgray;
|
||||
border-radius: 1.3px;
|
||||
border: 0.2px solid #010101;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 8.4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
background: lightgray;
|
||||
border-radius: 1.3px;
|
||||
border: 0.2px solid #010101;
|
||||
}
|
||||
|
||||
|
||||
input[type=range]::-moz-range-thumb {
|
||||
border: 1px solid #000000;
|
||||
height: 36px;
|
||||
width: 32px;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border: 1px solid #000000;
|
||||
height: 36px;
|
||||
width: 32px;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
margin-top: -14px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */
|
||||
}
|
||||
</style>
|
87
frontend/src/components/MasterPowerControlModule.vue
Normal file
87
frontend/src/components/MasterPowerControlModule.vue
Normal file
|
@ -0,0 +1,87 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import { ref, onMounted, reactive, watch } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/power/master/status',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const MasterStatus = ref(false)
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
MasterStatus.value = msg == '1'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
async function setMaster() {
|
||||
let topicPref = props.room + "/power/master/set"
|
||||
let command = '0'
|
||||
if (!MasterStatus.value) {
|
||||
command = '1'
|
||||
}
|
||||
publishMQTTMsg(topicPref, command)
|
||||
//audioStatus.value = command == '1'
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
|
||||
const roomState = ref(0)
|
||||
// OFF -> 0; ON -> 1; IN BETWEEN -> 2
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<h3>Sistem</h3>
|
||||
<button style="" @click="setMaster()">
|
||||
{{ MasterStatus ? 'IZKLOP' : 'VKLOP' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
123
frontend/src/components/Numpad.vue
Normal file
123
frontend/src/components/Numpad.vue
Normal file
|
@ -0,0 +1,123 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, computed} from 'vue'
|
||||
|
||||
const emit = defineEmits(['submitPasscode'])
|
||||
defineProps([])
|
||||
|
||||
// TODO: unhardocde this shit
|
||||
const correctCode = '1337'
|
||||
const passcodeLength = correctCode.length
|
||||
const enteredCode = ref('')
|
||||
const status = ref('')
|
||||
const showStatus = ref(false)
|
||||
|
||||
|
||||
const appendDigit = (digit: string) => {
|
||||
if (enteredCode.value.length < passcodeLength) {
|
||||
enteredCode.value += digit
|
||||
// console.log('entered code', enteredCode.value)
|
||||
}
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
enteredCode.value = ''
|
||||
status.value = ''
|
||||
}
|
||||
|
||||
const submit = () => {
|
||||
if (enteredCode.value === correctCode) {
|
||||
status.value = 'Access Granted'
|
||||
emit('submitPasscode', true)
|
||||
} else {
|
||||
status.value = 'Access Denied'
|
||||
enteredCode.value = "Access Denied"
|
||||
showStatus.value = true
|
||||
}
|
||||
setTimeout(() => {
|
||||
clear()
|
||||
showStatus.value = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="keypad">
|
||||
<div class="keypadFeedback">
|
||||
<span v-show="showStatus">Access Denied</span>
|
||||
<span v-show="!showStatus" v-for="(_, i) in passcodeLength" :key="i">
|
||||
<span>
|
||||
{{ enteredCode[i] ? '•' : ' ' }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="keypadButtons">
|
||||
<div class="keypadRow">
|
||||
<button @click="appendDigit('1')">1</button>
|
||||
<button @click="appendDigit('2')">2</button>
|
||||
<button @click="appendDigit('3')">3</button>
|
||||
</div>
|
||||
<div class="keypadRow">
|
||||
<button @click="appendDigit('4')">4</button>
|
||||
<button @click="appendDigit('5')">5</button>
|
||||
<button @click="appendDigit('6')">6</button>
|
||||
</div>
|
||||
<div class="keypadRow">
|
||||
<button @click="appendDigit('7')">7</button>
|
||||
<button @click="appendDigit('8')">8</button>
|
||||
<button @click="appendDigit('9')">9</button>
|
||||
</div>
|
||||
<div class="keypadRow">
|
||||
<button class="" @click="clear">Del</button>
|
||||
<button @click="appendDigit('0')">0</button>
|
||||
<button class="" @click="submit">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
margin: 0.1em;
|
||||
flex: max-content;
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
align-self: inherit;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.keypad {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1.5em;
|
||||
align-content: space-evenly;
|
||||
}
|
||||
|
||||
.keypadRow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
align-content: space-evenly;
|
||||
}
|
||||
|
||||
.keypadFeedback {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
background-color: lightgray;
|
||||
background-clip: border-box;
|
||||
font-size: 2em;
|
||||
min-height: 2em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
border: 1px solid #000000;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
</style>
|
|
@ -1,15 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
import {ref, onMounted, reactive} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import DownIcon from './icons/DownIcon.vue';
|
||||
import UpIcon from './icons/UpIcon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String,
|
||||
ctrlType: [String, null]
|
||||
room: String,
|
||||
position: String,
|
||||
ctrlType: [String, null]
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
|
@ -17,14 +17,14 @@ const topicstrs = [ //TODO everything else
|
|||
props.room + '/projectors/' + props.position + 'platno/status',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
|
@ -59,7 +59,7 @@ function handlePlatnoStatus(msg: string) {
|
|||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
|
@ -70,7 +70,6 @@ function publishMQTTMsg(topic: string, msg: string) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/platno/')
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
@ -87,31 +86,56 @@ const platnoStatus = ref(platnoState.UNKNOWN)
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- <h3>{{ props.room }}</h3> -->
|
||||
<!-- <form> -->
|
||||
<!-- TODO lepš -->
|
||||
<h4>platna {{ props.position }}</h4>
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'UP')"><UpIcon/></button>
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'DOWN')"><DownIcon/></button>
|
||||
<div v-if="props.ctrlType == 'service'">
|
||||
<h5>Manual control</h5>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move', 'UP')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move', 'STOP')" >
|
||||
<UpIcon /></button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move', 'DOWN')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move', 'STOP')" >
|
||||
<DownIcon /></button>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<!-- </form> -->
|
||||
<h4>Platno</h4>
|
||||
<div class="updown">
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'UP')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'DOWN')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="props.ctrlType == 'service'">
|
||||
<h5>Manual control</h5>
|
||||
|
||||
<div class="updown">
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move', 'UP')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'move', 'UP')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move', 'STOP')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'move', 'STOP')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move', 'DOWN')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'move', 'DOWN')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move', 'STOP')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'move', 'STOP')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 0.1rem;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.updown {
|
||||
display: flex
|
||||
}
|
||||
.updown button {
|
||||
flex: 1
|
||||
}
|
||||
</style>
|
87
frontend/src/components/ProjectorPowerModule.vue
Normal file
87
frontend/src/components/ProjectorPowerModule.vue
Normal file
|
@ -0,0 +1,87 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import { ref, onMounted, reactive, watch } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/power/projectors/status',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const projectorsStatus = ref(false)
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
projectorsStatus.value = msg == '1'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
async function setProjectors() {
|
||||
let topicPref = props.room + "/power/projectors/set"
|
||||
let command = '0'
|
||||
if (!projectorsStatus.value) {
|
||||
command = '1'
|
||||
}
|
||||
publishMQTTMsg(topicPref, command)
|
||||
//audioStatus.value = command == '1'
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
|
||||
const roomState = ref(0)
|
||||
// OFF -> 0; ON -> 1; IN BETWEEN -> 2
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<h3>Projektorji</h3>
|
||||
<button style="" @click="setProjectors()">
|
||||
{{ projectorsStatus ? 'IZKLOP' : 'VKLOP' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
63
frontend/src/components/ProjectorShutter.vue
Normal file
63
frontend/src/components/ProjectorShutter.vue
Normal file
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive } from "vue";
|
||||
import { $mqtt } from "vue-paho-mqtt";
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String,
|
||||
ctrltype: [String, null],
|
||||
});
|
||||
|
||||
const topicPrefix = `${props.room}/projectors/${props.position}`
|
||||
|
||||
const status = reactive({
|
||||
power: false,
|
||||
shutter: false,
|
||||
freeze: false,
|
||||
})
|
||||
|
||||
function bindBoolean(name: keyof typeof status) {
|
||||
$mqtt.subscribe(`${topicPrefix}/status/${name}`, (message) => {
|
||||
status[name] = message == "1";
|
||||
console.debug(name, "=", message == "1")
|
||||
});
|
||||
}
|
||||
|
||||
bindBoolean("power")
|
||||
bindBoolean("shutter")
|
||||
bindBoolean("freeze")
|
||||
|
||||
function doCommand(command: keyof typeof status, shutter: boolean) {
|
||||
$mqtt.publish(`${topicPrefix}/set/${command}`, shutter ? "1" : "0", 'Fnr');
|
||||
console.debug("SET SHUTTER", shutter)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="btns">
|
||||
<button @click="doCommand('shutter', !status.shutter)" :class="{ disabled: !status.power, currentlyActive: status.power && status.shutter }">
|
||||
SHUTTER
|
||||
</button>
|
||||
<button @click="doCommand('freeze', !status.freeze)" :class="{ disabled: !status.power, currentlyActive: status.power && status.freeze }">
|
||||
FREEZE
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.btns {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: .5em;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
min-width: 40%;
|
||||
}
|
||||
</style>
|
|
@ -7,15 +7,18 @@ import { $mqtt } from 'vue-paho-mqtt'
|
|||
const props = defineProps({
|
||||
room: String,
|
||||
position: String,
|
||||
ctrlType: [String, null]
|
||||
ctrltype: [String, null]
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/projectors/' + props.position + '/status/power',
|
||||
props.room + '/projectors/' + props.position + '/status/shutter',
|
||||
props.room + '/projectors/' + props.position + '/status/freeze',
|
||||
props.room + '/projectors/' + props.position + '/status',
|
||||
]
|
||||
|
||||
const isUnreachable = ref(false)
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
|
@ -27,16 +30,20 @@ const subscriptions =
|
|||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
if (topic.includes('status')) {
|
||||
if (topic.includes('status') && !topic.endsWith('status')) {
|
||||
//console.log(topic.split('/'))
|
||||
let typ = topic.split('/')[4]
|
||||
handleProjectorStatus(typ, msg)
|
||||
} else {
|
||||
isUnreachable.value = msg.length > 1
|
||||
}
|
||||
}
|
||||
|
||||
function handleProjectorStatus(typ: string, msg: string) {
|
||||
console.log('handling status')
|
||||
//console.log(projStatus)
|
||||
console.debug(props.room, projStatus.power, projStatus.shutter)
|
||||
|
||||
if (typ == 'power') { projStatus.power = msg == '1' }
|
||||
else if (typ == 'shutter') { projStatus.shutter = msg == '1' }
|
||||
else if (typ == 'freeze') { projStatus.freeze = msg == '1' }
|
||||
|
@ -51,13 +58,13 @@ onMounted(() => {
|
|||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
$mqtt.publish(topic, msg, 'Fnr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
|
||||
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/command/')
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/set/')
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
|
@ -71,21 +78,22 @@ const projStatus = reactive({
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<span v-if="isUnreachable">CONNECTION FAILED</span>
|
||||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<h5>Power: {{ (projStatus.power ? "ON" : "OFF") }}</h5>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'power', (!projStatus.power ? '1' : '0'))">
|
||||
<h4>Power</h4>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'power', (!projStatus.power ? '1' : '0'))" :class="{currentlyActive: projStatus.power}">
|
||||
Turn {{ projStatus.power ? 'OFF' : 'ON' }}</button>
|
||||
</div>
|
||||
<div :class="{ disabled: !projStatus.power }">
|
||||
<div>
|
||||
<h5>Shutter: {{ (projStatus.shutter ? "ON" : "OFF") }}</h5>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'shutter', (!projStatus.shutter ? '1' : '0'))">
|
||||
<h4>Shutter</h4>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'shutter', (!projStatus.shutter ? '1' : '0'))" :class="{currentlyActive: projStatus.shutter}">
|
||||
Turn {{ projStatus.shutter ? 'OFF' : 'ON' }}</button>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Freeze image: {{ (projStatus.freeze ? "ON" : "OFF") }}</h5>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'freeze', (!projStatus.freeze ? '1' : '0'))">
|
||||
<h4>Freeze image</h4>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'freeze', (!projStatus.freeze ? '1' : '0'))" :class="{currentlyActive: projStatus.freeze}">
|
||||
Turn {{ projStatus.freeze ? 'OFF' : 'ON' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -94,7 +102,7 @@ const projStatus = reactive({
|
|||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
83
frontend/src/components/ResetButton.vue
Normal file
83
frontend/src/components/ResetButton.vue
Normal file
|
@ -0,0 +1,83 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import { ref, onMounted, reactive, watch } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const audioStatus = ref(false)
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
audioStatus.value = msg == '1'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
async function resetAll() {
|
||||
let topicPref = props.room + "/power/"
|
||||
publishMQTTMsg(topicPref + 'audio/set', '0')
|
||||
publishMQTTMsg(topicPref + 'projectors/set', '1')
|
||||
publishMQTTMsg(topicPref + 'master/set', '1')
|
||||
publishMQTTMsg(props.room + "/projectors/main/lift/move/up", '1')
|
||||
publishMQTTMsg(props.room + "/projectors/side/lift/move/up", '1')
|
||||
//TODO NOT HARDCODE THISEFEWGJREWGREW
|
||||
publishMQTTMsg(props.room + "/projectors/main/platno/goto", 'UP')
|
||||
publishMQTTMsg(props.room + "/projectors/side/platno/goto", 'UP')
|
||||
//audioStatus.value = command == '1'
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<h3>Reset sist.</h3>
|
||||
<button style="" @click="resetAll()">
|
||||
RESET
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,3 +1,3 @@
|
|||
<template>
|
||||
🢃
|
||||
▼
|
||||
</template>
|
|
@ -1,3 +1,3 @@
|
|||
<template>
|
||||
🢁
|
||||
▲
|
||||
</template>
|
53
frontend/src/components/pages/AudioPage.vue
Normal file
53
frontend/src/components/pages/AudioPage.vue
Normal file
|
@ -0,0 +1,53 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
import DownIcon from '../icons/DownIcon.vue';
|
||||
import UpIcon from '../icons/UpIcon.vue';
|
||||
import AudioControlModule from "@/components/AudioControlModule.vue";
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/power/audio/set'
|
||||
]
|
||||
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div style="display:flex; gap: 1rem">
|
||||
<AudioControlModule :room="room" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 0.1rem;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.currentlySelectedPreset {
|
||||
background-color: orange;
|
||||
}
|
||||
</style>
|
237
frontend/src/components/pages/LightingPage.vue
Normal file
237
frontend/src/components/pages/LightingPage.vue
Normal file
|
@ -0,0 +1,237 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import DownIcon from '../icons/DownIcon.vue';
|
||||
import UpIcon from '../icons/UpIcon.vue';
|
||||
import LightControl from '../LightControl.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/shades/status',
|
||||
props.room + '/lucke/preset/current',
|
||||
]
|
||||
|
||||
const lastPreset = ref("-1")
|
||||
const platnoStatus = ref("UNKNOWN")
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
if (topic.includes('status')) {
|
||||
//console.log(topic.split('/'))
|
||||
let typ = topic.split('/')[4]
|
||||
handlePlatnoStatus(msg)
|
||||
} else if (topic.includes('current')) {
|
||||
lastPreset.value = msg
|
||||
}
|
||||
}
|
||||
|
||||
enum firankState {
|
||||
UP,
|
||||
DOWN,
|
||||
MOVING,
|
||||
STOPPED
|
||||
}
|
||||
|
||||
const firankStatus = ref(firankState.STOPPED)
|
||||
|
||||
function handlePlatnoStatus(msg: string) {
|
||||
console.log('handling status')
|
||||
//console.log(projStatus)
|
||||
platnoStatus.value = msg
|
||||
let newState: firankState
|
||||
switch (msg) {
|
||||
case "UP":
|
||||
newState = firankState.UP
|
||||
break
|
||||
case "DOWN":
|
||||
newState = firankState.DOWN
|
||||
break
|
||||
case "MOVING":
|
||||
newState = firankState.MOVING
|
||||
break
|
||||
default:
|
||||
newState = firankState.STOPPED
|
||||
break
|
||||
}
|
||||
firankStatus.value = newState
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
const publishPrefix = ref(props.room + '/')
|
||||
|
||||
// TODO: unhardcode
|
||||
const showCustom = true;
|
||||
|
||||
// TODO: un-hard-code this
|
||||
const lights = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Tabla",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 5,
|
||||
name: "Začetek",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 2,
|
||||
name: "Sredina",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 3,
|
||||
name: "Vrh",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 4,
|
||||
name: "Vhod 1",
|
||||
dimmable: false,
|
||||
}, {
|
||||
id: 7,
|
||||
name: "Vhod 2",
|
||||
dimmable: false,
|
||||
// }, {
|
||||
// id: 6,
|
||||
// name: "Reflektorji 2 (ne dela)",
|
||||
// dimmable: false,
|
||||
}, {
|
||||
id: 8,
|
||||
name: "Stopnice",
|
||||
dimmable: false,
|
||||
}
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
|
||||
<div class="razsvetljava">
|
||||
<div class="lightButtons" style="display: flex; flex-direction: column; align-items: stretch">
|
||||
<div class="row">
|
||||
<button :class="{currentlyActive: (lastPreset == '4')}"
|
||||
@click="publishMQTTMsg(publishPrefix + 'lucke/preset/recall', '4')">IZKLOP
|
||||
</button>
|
||||
<button :class="{currentlyActive: (lastPreset == '3')}"
|
||||
@click="publishMQTTMsg(publishPrefix + 'lucke/preset/recall', '3')">PRIPRAVA
|
||||
</button>
|
||||
<div class="editablePreset">
|
||||
<button :class="{currentlyActive: (lastPreset == '5')}"
|
||||
@click="publishMQTTMsg(publishPrefix + 'lucke/preset/recall', '5')"
|
||||
v-if="showCustom">
|
||||
CUSTOM 5
|
||||
</button>
|
||||
<button class="saveButton" @click="publishMQTTMsg(publishPrefix + 'lucke/preset/save', '5')">💾</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<button :class="{currentlyActive: (lastPreset == '2')}"
|
||||
@click="publishMQTTMsg(publishPrefix + 'lucke/preset/recall', '2')">PREDAVANJE
|
||||
</button>
|
||||
<button :class="{currentlyActive: (lastPreset == '1')}"
|
||||
@click="publishMQTTMsg(publishPrefix + 'lucke/preset/recall', '1')">PROJEKCIJA
|
||||
</button>
|
||||
<div class="editablePreset">
|
||||
<button :class="{currentlyActive: (lastPreset == '6')}"
|
||||
@click="publishMQTTMsg(publishPrefix + 'lucke/preset/recall', '6')"
|
||||
v-if="showCustom">
|
||||
CUSTOM 6
|
||||
</button>
|
||||
<button class="saveButton" @click="publishMQTTMsg(publishPrefix + 'lucke/preset/save', '6')">💾</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="justify-content: center">
|
||||
<LightControl v-for="light in lights" :key="light.id" v-bind="light" :room="room" />
|
||||
</div>
|
||||
|
||||
<div style="justify-content: center" class="sencila">
|
||||
<h3>Senčila</h3>
|
||||
<button
|
||||
:class="{currentlyActive: (platnoStatus == 'MOVING_UP')}"
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'shades/move', 'MOVE_UP')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'shades/move', 'MOVE_UP')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'shades/move', 'STOP')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'shades/move', 'STOP')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
:class="{currentlyActive: (platnoStatus == 'MOVING_DOWN')}"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'shades/move', 'MOVE_DOWN')"
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'shades/move', 'MOVE_DOWN')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'shades/move', 'STOP')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'shades/move', 'STOP')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.sencila button {
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 0.3rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.row > * {
|
||||
flex:1
|
||||
}
|
||||
|
||||
.editablePreset {
|
||||
display: flex
|
||||
}
|
||||
|
||||
.lightButtons button {
|
||||
width: fit-content;
|
||||
flex: 1;
|
||||
height: 3.5em;
|
||||
}
|
||||
|
||||
|
||||
button.saveButton {
|
||||
flex: 0;
|
||||
padding: .5rem;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
</style>
|
36
frontend/src/components/pages/MainPage.vue
Normal file
36
frontend/src/components/pages/MainPage.vue
Normal file
|
@ -0,0 +1,36 @@
|
|||
<script setup lang="ts">
|
||||
import LectureModule from '../LectureModule.vue';
|
||||
import AudioControlModule from '../AudioControlModule.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div style="display: flex; gap: 1rem; margin:inherit">
|
||||
<div style="width: 50%;">
|
||||
<h3>Glavni projektor</h3>
|
||||
<LectureModule :room="props.room" position="main"/>
|
||||
</div>
|
||||
<div style="width: 50%;">
|
||||
<h3>Stranski projektor</h3>
|
||||
<LectureModule :room="props.room" position="side"/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: center">
|
||||
<AudioControlModule :room="props.room" :big="true"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
</style>
|
74
frontend/src/components/pages/ServisPage.vue
Normal file
74
frontend/src/components/pages/ServisPage.vue
Normal file
|
@ -0,0 +1,74 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, onMounted, reactive, watch} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import Platno from '../Platno.vue'
|
||||
import Lift from '../Lift.vue';
|
||||
import Projektor from '../Projektor.vue';
|
||||
import ProjectorPowerModule from "@/components/ProjectorPowerModule.vue";
|
||||
import AudioControlModule from "@/components/AudioControlModule.vue";
|
||||
import MasterPowerControlModule from "@/components/MasterPowerControlModule.vue";
|
||||
import ResetButton from "@/components/ResetButton.vue";
|
||||
import Numpad from "@/components/Numpad.vue";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
currentlyActive: Boolean,
|
||||
})
|
||||
const _glavni_position = ref('main')
|
||||
const _stranski_position = ref('side')
|
||||
const _ctrl_type = ref('service')
|
||||
|
||||
const showServis = ref(false)
|
||||
|
||||
|
||||
|
||||
watch(props, (ca) => {
|
||||
if (!props.currentlyActive) {
|
||||
showServis.value = false
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-show="!showServis" style="display: flex">
|
||||
<Numpad @submit-passcode="(b: boolean) => showServis = b"/>
|
||||
</div>
|
||||
<div v-show="showServis" style="display: flex; gap: 1rem">
|
||||
<div>
|
||||
<Projektor :room="props.room" :position="_glavni_position" :ctrlType="_ctrl_type"/>
|
||||
<Lift :room="props.room" :position="_glavni_position"/>
|
||||
<Platno :room="props.room" :position="_glavni_position" :ctrlType="_ctrl_type"/>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<Projektor :room="props.room" :position="_stranski_position" :ctrlType="_ctrl_type"/>
|
||||
<Lift :room="props.room" :position="_stranski_position"/>
|
||||
<Platno :room="props.room" :position="_stranski_position" :ctrlType="_ctrl_type"/>
|
||||
</div>
|
||||
<div>
|
||||
<h5>POWER CONTROL</h5>
|
||||
<div>
|
||||
<ProjectorPowerModule :room="props.room"/>
|
||||
<AudioControlModule :room="props.room"/>
|
||||
<MasterPowerControlModule :room="props.room"/>
|
||||
</div>
|
||||
<div></div>
|
||||
<ResetButton :room="props.room"/>
|
||||
<button onclick="window.location.reload()">RELOAD PAGE</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
<!-- scaling UIja, buttons as big as can, text as big (within reason), clear feedback, mqtt defaults to host font family noto sans, sans serif, chrome disable -->
|
||||
<!-- naj se odziva ono sam na platno status fuj fej -->
|
|
@ -9,27 +9,34 @@ import Platno from '../Platno.vue'
|
|||
const props = defineProps({
|
||||
room: String,
|
||||
})
|
||||
const _glavni_position = ref('glavni')
|
||||
const _stranski_position = ref('stranski')
|
||||
const _glavni_position = ref('main')
|
||||
const _stranski_position = ref('side')
|
||||
const _test = ref('test')
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="display: flex; gap: 1rem">
|
||||
<div>
|
||||
<h4>Glavni</h4>
|
||||
<div class="page">
|
||||
<div class="col">
|
||||
<h3>Glavni</h3>
|
||||
<Projektor :room="props.room" :position="_glavni_position" :ctrl-type="_test" />
|
||||
<Platno :room="props.room" :position="_glavni_position" />
|
||||
</div>
|
||||
<div>
|
||||
<h4>Stranski</h4>
|
||||
<Projektor :room="props.room" :position="_stranski_position" />
|
||||
<Platno :room="props.room" :position="_stranski_position" />
|
||||
<Platno :room="props.room" :position="_glavni_position" :ctrl-type="_test" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<h3>Stranski</h3>
|
||||
<Projektor :room="props.room" :position="_stranski_position" :ctrltype="_test"/>
|
||||
<Platno :room="props.room" :position="_stranski_position" :ctrl-type="_test"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
.col {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
39
frontend/src/components/tabs/Tab.vue
Normal file
39
frontend/src/components/tabs/Tab.vue
Normal file
|
@ -0,0 +1,39 @@
|
|||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
selected: Boolean,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="tab" :class="{ selected: props.selected }">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tab {
|
||||
background-color: var(--color-brand-ul-medium-grey);
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
|
||||
border: 1px solid #000000;
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
border-left: none;
|
||||
background: lightgray;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
.tab.selected {
|
||||
background-color: var(--color-brand-ul-red);
|
||||
}
|
||||
|
||||
.vertical-tabs .tab {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.vertical-tabs .tab:not(:last-child) {
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
</style>
|
16
frontend/src/components/tabs/VerticalTabs.vue
Normal file
16
frontend/src/components/tabs/VerticalTabs.vue
Normal file
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<div class="vertical-tabs">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vertical-tabs .tab {
|
||||
width: 100%;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
.vertical-tabs .tab:not(:last-child) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
|
@ -10,20 +10,21 @@ createApp(App)
|
|||
createPahoMqttPlugin({
|
||||
PluginOptions: {
|
||||
autoConnect: true,
|
||||
showNotifications: true,
|
||||
showNotifications: false,
|
||||
},
|
||||
|
||||
|
||||
MqttOptions: {
|
||||
//host: import.meta.env.VITE_MQTT_HOST,
|
||||
//host: "localhost",
|
||||
host: urlParams.get('mqtt') || 'localhost',
|
||||
host: urlParams.get('mqtt') || window.location.hostname,
|
||||
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}`,
|
||||
//mainTopic: '',
|
||||
enableMainTopic: false
|
||||
enableMainTopic: false,
|
||||
|
||||
},
|
||||
})
|
||||
)
|
3
frontend/vju_display/.vscode/extensions.json
vendored
3
frontend/vju_display/.vscode/extensions.json
vendored
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
# vju_display
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
|
@ -1,103 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import MainPage from './components/pages/MainPage.vue';
|
||||
import VideoPage from './components/pages/VideoPage.vue';
|
||||
import VerticalTabs from './components/tabs/VerticalTabs.vue';
|
||||
import Tab from './components/tabs/Tab.vue';
|
||||
import LightingPage from './components/pages/LightingPage.vue';
|
||||
import ServisPage from './components/pages/ServisPage.vue';
|
||||
|
||||
let urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
const currentRoom = ref(urlParams.get('room') || 'none') // if no param specified
|
||||
|
||||
// should also check if valid room parameter ampak se ne mudi
|
||||
// TODO does this make sense al se naj naprej defaulta kr na p01?
|
||||
|
||||
|
||||
const pageNum = ref(0)
|
||||
const srvcUnlocked = ref(false)
|
||||
const showPinPopup = ref(false)
|
||||
|
||||
function openPinDiag() {
|
||||
|
||||
}
|
||||
|
||||
function closePinDiag() {
|
||||
|
||||
}
|
||||
|
||||
function unlockServicePage() {
|
||||
showPinPopup.value = true
|
||||
while (true) {
|
||||
if ('' == '') {
|
||||
srvcUnlocked.value = true
|
||||
closePinDiag()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lockServicePage() {
|
||||
srvcUnlocked.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="currentRoom == 'none'">
|
||||
<h1>Incorrect or missing room parameter!</h1>
|
||||
</div>
|
||||
<div v-else id="wrapper">
|
||||
<header class="sidebar">
|
||||
|
||||
<img class="logo" src="https://fri.uni-lj.si/sites/all/themes/fri_theme/images/fri_logo.png" />
|
||||
<h1>{{ currentRoom.toUpperCase() }}</h1>
|
||||
|
||||
<VerticalTabs id="nav">
|
||||
<Tab @click="pageNum=0; lockServicePage()" :selected="pageNum==0">Priprava</Tab>
|
||||
<Tab @click="pageNum=1; lockServicePage()" :selected="pageNum==1">Video</Tab>
|
||||
<Tab @click="pageNum=2; lockServicePage()" :selected="pageNum==2">Audio</Tab>
|
||||
<Tab @click="pageNum=3; lockServicePage()" :selected="pageNum==3">Lučke</Tab>
|
||||
<Tab @click="pageNum=4; unlockServicePage()" :selected="pageNum==4">Servis</Tab>
|
||||
</VerticalTabs>
|
||||
<large style="display: flex;">turbo odličen super mega kontrol panel</large>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<MainPage v-if="pageNum == 0" :room="currentRoom" />
|
||||
<VideoPage v-else-if="pageNum == 1" :room="currentRoom" />
|
||||
|
||||
<LightingPage v-else-if="pageNum == 3" :room="currentRoom" />
|
||||
|
||||
<ServisPage v-else-if="pageNum == 4 && srvcUnlocked" :room="currentRoom" />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
max-width: 20vw;
|
||||
}
|
||||
.logo {
|
||||
max-width: 100%;
|
||||
padding: .4rem;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,32 +0,0 @@
|
|||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-text: #000;
|
||||
--color-background: #EEE;
|
||||
|
||||
--color-brand-ul-red: #e03127;
|
||||
--color-brand-ul-light-grey: #E6E7E8;
|
||||
--color-brand-ul-medium-grey: #A7A8AA;
|
||||
--color-brand-ul-dark-grey: #58595b;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family: sans-serif;
|
||||
font-size: 17px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
@import './base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
background-color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover, button:focus, button:active {
|
||||
background-color: #ccc;
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import { ref, onMounted, reactive, watch } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String,
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/projectors/' + props.position + '/status/power',
|
||||
props.room + '/projectors/' + props.position + '/platno/status',
|
||||
props.room + '/projectors/' + props.position + '/lift/status'
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
let typ: string = ''
|
||||
if (topic.includes('power')) {
|
||||
typ = topic.split('/')[4]
|
||||
} else if (topic.includes('platno')) {
|
||||
typ = topic.split('/')[3]
|
||||
} else if (topic.includes('lift')) {
|
||||
typ = topic.split('/')[3]
|
||||
} else { return }
|
||||
handleIncStatus(typ, msg)
|
||||
}
|
||||
|
||||
function handleIncStatus(typ: string, msg: string) {
|
||||
console.log('handling status')
|
||||
//console.log(projStatus)
|
||||
if (typ == 'power') { roomStatus.proj_power = msg == '1' }
|
||||
else if (typ == 'platno') { roomStatus.platno_state = msg }
|
||||
else if (typ == 'lift') { roomStatus.lift_state = msg }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, ms)
|
||||
})
|
||||
}
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
async function setLecture(pos?: String) {
|
||||
if (!pos) { return }
|
||||
let topicPref = props.room + "/projektorji/" + props.position + "/"
|
||||
let command = '0'
|
||||
if (roomState.value == 0) {
|
||||
command = '1'
|
||||
} else if (roomState.value == 1) {
|
||||
command = '0'
|
||||
} else { return }
|
||||
publishMQTTMsg((topicPref + 'set/power'), command)
|
||||
publishMQTTMsg((topicPref + 'platno/goto'), command == '1' ? 'DOWN' : 'UP')
|
||||
publishMQTTMsg((topicPref + 'lift/move/' + (command == '1' ? 'down' : 'up') ), command)
|
||||
await sleep(500)
|
||||
publishMQTTMsg((topicPref + 'platno/goto'), 'STOP')
|
||||
publishMQTTMsg((topicPref + 'lift/move/' + (command == '1' ? 'down' : 'up') ), '0')
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
const roomStatus = reactive({
|
||||
proj_power: false,
|
||||
lift_state: 'UNKNOWN',
|
||||
platno_state: 'UNKNOWN',
|
||||
})
|
||||
|
||||
const roomState = ref(0)
|
||||
// OFF -> 0; ON -> 1; IN BETWEEN -> 2
|
||||
|
||||
watch (roomStatus, (_, _newState) => {
|
||||
if (!_newState) { return }
|
||||
let allOn = _newState.proj_power && _newState.platno_state == 'DOWN' && _newState.lift_state == 'DOWN'
|
||||
let allOff = !_newState.proj_power && _newState.platno_state == 'UP' && _newState.lift_state == 'UP'
|
||||
if (allOn) {
|
||||
roomState.value = 1
|
||||
} else if (allOff) {
|
||||
roomState.value = 0
|
||||
} else {
|
||||
roomState.value = 2
|
||||
}
|
||||
}, {immediate: true})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<h3 v-if="roomState == 1">AKTIVNO</h3>
|
||||
<h3 v-else-if="roomState == 0">V PRIPRAVLJENOSTI</h3>
|
||||
<h3 v-else-if="roomState == 2">POČAKAJTE</h3>
|
||||
<h3 v-else="roomState == 1">NAPAKA</h3>
|
||||
<button style="" @click="setLecture(props.position)" :disabled="roomState == 2">
|
||||
{{ roomState == 1 ? 'UGASNI' : (roomState == 0 ? 'PRIŽGI' : '...') }}
|
||||
</button>
|
||||
</div>
|
||||
<h6>Projektor status: {{ roomStatus.proj_power ? "ON" : "OFF" }}</h6>
|
||||
<h6>Platno pozicija: {{ roomStatus.platno_state }}</h6>
|
||||
<h6>Dvigalo pozicija: {{ roomStatus.lift_state }}</h6>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,125 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
import DownIcon from './icons/DownIcon.vue';
|
||||
import UpIcon from './icons/UpIcon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/projectors/' + props.position + 'lift/status',
|
||||
props.room + '/projectors/' + props.position + 'lift/status',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
if (topic.includes('status')) {
|
||||
//console.log(topic.split('/'))
|
||||
let typ = topic.split('/')[4]
|
||||
// handlePlatnoStatus(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// enum firankState {
|
||||
// UP,
|
||||
// DOWN,
|
||||
// MOVING,
|
||||
// STOPPED
|
||||
// }
|
||||
// const firankStatus = ref(firankState.STOPPED)
|
||||
|
||||
// function handlePlatnoStatus(msg: string) {
|
||||
// console.log('handling status')
|
||||
// //console.log(projStatus)
|
||||
// let newState: firankState
|
||||
// switch (msg) {
|
||||
// case "UP":
|
||||
// newState = firankState.UP
|
||||
// break
|
||||
// case "DOWN":
|
||||
// newState = firankState.DOWN
|
||||
// break
|
||||
// case "MOVING":
|
||||
// newState = firankState.MOVING
|
||||
// break
|
||||
// default:
|
||||
// newState = firankState.STOPPED
|
||||
// break
|
||||
// }
|
||||
// firankStatus.value = newState
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/lift/')
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<!--
|
||||
TODO: NE HARDCODANO
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div style="display:flex; gap: 1rem">
|
||||
<div>
|
||||
<h4>Lifti {{ props.position }}</h4>
|
||||
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/up', '0')" >
|
||||
<UpIcon /></button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/down', '0')" >
|
||||
<DownIcon /></button>
|
||||
</div><div>
|
||||
<h4>Servis lifti {{ props.position }}</h4>
|
||||
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/service_up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/service_up', '0')" >
|
||||
<UpIcon /></button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/service_down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/service_down', '0')" >
|
||||
<DownIcon /></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 0.1rem;
|
||||
width: 45%;
|
||||
}
|
||||
</style>
|
|
@ -1,113 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
import DownIcon from '../icons/DownIcon.vue';
|
||||
import UpIcon from '../icons/UpIcon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/projectors/' + props.position + 'platno/status',
|
||||
props.room + '/projectors/' + props.position + 'platno/status',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
if (topic.includes('status')) {
|
||||
//console.log(topic.split('/'))
|
||||
let typ = topic.split('/')[4]
|
||||
handlePlatnoStatus(msg)
|
||||
}
|
||||
}
|
||||
|
||||
enum firankState {
|
||||
UP,
|
||||
DOWN,
|
||||
MOVING,
|
||||
STOPPED
|
||||
}
|
||||
const firankStatus = ref(firankState.STOPPED)
|
||||
|
||||
function handlePlatnoStatus(msg: string) {
|
||||
console.log('handling status')
|
||||
//console.log(projStatus)
|
||||
let newState: firankState
|
||||
switch (msg) {
|
||||
case "UP":
|
||||
newState = firankState.UP
|
||||
break
|
||||
case "DOWN":
|
||||
newState = firankState.DOWN
|
||||
break
|
||||
case "MOVING":
|
||||
newState = firankState.MOVING
|
||||
break
|
||||
default:
|
||||
newState = firankState.STOPPED
|
||||
break
|
||||
}
|
||||
firankStatus.value = newState
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
})
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Qr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
}
|
||||
const publishPrefix = ref(props.room + '/firanki/')
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="display: flex; gap: 1rem">
|
||||
|
||||
</div>
|
||||
<div style="display:flex; gap: 1rem">
|
||||
<div>
|
||||
<h4>Firanki</h4>
|
||||
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move', 'MOVE_UP')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move', 'STOP')" >
|
||||
<UpIcon /></button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move', 'MOVE_DOWN')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move', 'STOP')" >
|
||||
<DownIcon /></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: .8;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 0.1rem;
|
||||
width: 45%;
|
||||
}
|
||||
</style>
|
|
@ -1,32 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, onMounted, reactive } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
import Projektor from '../Projektor.vue';
|
||||
import Platno from '../Platno.vue';
|
||||
import Lift from '../Lift.vue';
|
||||
import LectureModule from '../LectureModule.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String
|
||||
})
|
||||
|
||||
const _glavni_position = ref('glavni')
|
||||
const _stranski_position = ref('stranski')
|
||||
|
||||
let _glavni = ref('glavni')
|
||||
let _stranski = ref('stranski')
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="display:flex; gap: 1rem">
|
||||
<div style="width: 50%;">
|
||||
<h4>Glavni:</h4>
|
||||
<LectureModule :room="props.room" :position="_glavni" />
|
||||
</div>
|
||||
<div style="width: 50%;">
|
||||
<h4>Stranski:</h4>
|
||||
<LectureModule :room="props.room" :position="_stranski" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,37 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive } from 'vue'
|
||||
import { $mqtt } from 'vue-paho-mqtt'
|
||||
// import Projektor from '../Projektor.vue'
|
||||
// import Platno from '../Platno.vue'
|
||||
import Lift from '../Lift.vue';
|
||||
import Projektor from '../Projektor.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
})
|
||||
const _glavni_position = ref('glavni')
|
||||
const _stranski_position = ref('stranski')
|
||||
const _ctrl_type = ref('service')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="display: flex; gap: 1rem">
|
||||
<div>
|
||||
<Projektor :room="props.room" :position="_glavni_position" :ctrlType="_ctrl_type" />
|
||||
<Lift :room="props.room" :position="_glavni_position" />
|
||||
<Platno :room="props.room" :position="_glavni_position" :ctrlType="_ctrl_type"/>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<Projektor :room="props.room" :position="_stranski_position" :ctrlType="_ctrl_type"/>
|
||||
<Lift :room="props.room" :position="_stranski_position" />
|
||||
<Platno :room="props.room" :position="_stranski_position" :ctrlType="_ctrl_type" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,37 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
selected: Boolean
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="tab" :class="{selected: props.selected}">
|
||||
<slot/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tab {
|
||||
background-color: var(--color-brand-ul-medium-grey);
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tab:hover {
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.tab.selected {
|
||||
background-color: var(--color-brand-ul-red);
|
||||
}
|
||||
|
||||
.vertical-tabs .tab {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.vertical-tabs .tab:not(:last-child) {
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
|
||||
<template>
|
||||
<div class="vertical-tabs">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tab:hover {
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.vertical-tabs .tab {
|
||||
width: 100%;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
.vertical-tabs .tab:not(:last-child) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
|
||||
poetry run python3 ~/unit_generator.py
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
poetry install
|
||||
cp ./generator_wrapper.sh /usr/lib/systemd/systemd-generators
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# interfaces(5) file used by ifup(8) and ifdown(8)
|
||||
# Include files from /etc/network/interfaces.d:
|
||||
source /etc/network/interfaces.d/*
|
|
@ -1,11 +0,0 @@
|
|||
[Unit]
|
||||
Description=Sets up MQTT topics
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
ExecStart =/usr/bin/python3 /home/rpi/mqtt_init_topics.py
|
||||
Type=simple
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
154
poetry.lock
generated
154
poetry.lock
generated
|
@ -1,154 +0,0 @@
|
|||
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiomqtt"
|
||||
version = "2.3.0"
|
||||
description = "The idiomatic asyncio MQTT client, wrapped around paho-mqtt"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8"
|
||||
files = [
|
||||
{file = "aiomqtt-2.3.0-py3-none-any.whl", hash = "sha256:127926717bd6b012d1630f9087f24552eb9c4af58205bc2964f09d6e304f7e63"},
|
||||
{file = "aiomqtt-2.3.0.tar.gz", hash = "sha256:312feebe20bc76dc7c20916663011f3bd37aa6f42f9f687a19a1c58308d80d47"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
paho-mqtt = ">=2.1.0,<3.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "aioserial"
|
||||
version = "1.3.1"
|
||||
description = "An asynchronous serial port library of Python"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
files = [
|
||||
{file = "aioserial-1.3.1.tar.gz", hash = "sha256:702bf03b0eb84b8ef2d8dac5cb925e1e685dce98f77b125569bc6fd2b3b58228"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyserial = "*"
|
||||
|
||||
[[package]]
|
||||
name = "colorzero"
|
||||
version = "2.0"
|
||||
description = "Yet another Python color library"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "colorzero-2.0-py2.py3-none-any.whl", hash = "sha256:0e60d743a6b8071498a56465f7719c96a5e92928f858bab1be2a0d606c9aa0f8"},
|
||||
{file = "colorzero-2.0.tar.gz", hash = "sha256:e7d5a5c26cd0dc37b164ebefc609f388de24f8593b659191e12d85f8f9d5eb58"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
setuptools = "*"
|
||||
|
||||
[package.extras]
|
||||
doc = ["pkginfo", "sphinx", "sphinx-rtd-theme"]
|
||||
test = ["pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "gpiozero"
|
||||
version = "2.0.1"
|
||||
description = "A simple interface to GPIO devices with Raspberry Pi"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "gpiozero-2.0.1-py3-none-any.whl", hash = "sha256:8f621de357171d574c0b7ea0e358cb66e560818a47b0eeedf41ce1cdbd20c70b"},
|
||||
{file = "gpiozero-2.0.1.tar.gz", hash = "sha256:d4ea1952689ec7e331f9d4ebc9adb15f1d01c2c9dcfabb72e752c9869ab7e97e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorzero = "*"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx (>=4.0)", "sphinx-rtd-theme (>=1.0)"]
|
||||
test = ["pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "paho-mqtt"
|
||||
version = "2.1.0"
|
||||
description = "MQTT version 5.0/3.1.1 client class"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee"},
|
||||
{file = "paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
proxy = ["pysocks"]
|
||||
|
||||
[[package]]
|
||||
name = "pyserial"
|
||||
version = "3.5"
|
||||
description = "Python Serial Port Extension"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"},
|
||||
{file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cp2110 = ["hidapi"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.6.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"},
|
||||
{file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"]
|
||||
core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
||||
cover = ["pytest-cov"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
||||
enabler = ["pytest-enabler (>=2.2)"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
|
||||
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "smbus2"
|
||||
version = "0.5.0"
|
||||
description = "smbus2 is a drop-in replacement for smbus-cffi/smbus-python in pure Python"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "smbus2-0.5.0-py2.py3-none-any.whl", hash = "sha256:1a15c3b9fa69357beb038cc0b5d37939702f8bfde1ddc89ca9f17d8461dbe949"},
|
||||
{file = "smbus2-0.5.0.tar.gz", hash = "sha256:4a5946fd82277870c2878befdb1a29bb28d15cda14ea4d8d2d54cf3d4bdcb035"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=1.5.3)"]
|
||||
qa = ["flake8"]
|
||||
|
||||
[[package]]
|
||||
name = "telnetlib3"
|
||||
version = "2.0.4"
|
||||
description = "Python 3 asyncio Telnet server and client Protocol library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "telnetlib3-2.0.4-py2.py3-none-any.whl", hash = "sha256:b3c0f984a7fb1b6ee16e6fdaa410c56389b0dc492174a99c6661b1ba4c9d457d"},
|
||||
{file = "telnetlib3-2.0.4.tar.gz", hash = "sha256:dbcbc16456a0e03a62431be7cfefff00515ab2f4ce2afbaf0d3a0e51a98c948d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "5e30fcfae3a69f8475da0f4ba655b5194a80b2a2185c652cbce2c72fa83aa752"
|
|
@ -1,13 +0,0 @@
|
|||
[Unit]
|
||||
Description=Control for projector motors
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/python3 /home/rpi/projector_motors.py
|
||||
Restart=always
|
||||
Type=simple
|
||||
User=kat
|
||||
Group=kat
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,105 +0,0 @@
|
|||
#import gpiozero.pins.mock
|
||||
#from gpiozero import *
|
||||
#from grove.factory import Factory
|
||||
#from grove.grove_relay import GroveRelay
|
||||
import aiomqtt
|
||||
import asyncio
|
||||
#from i2cpy import I2C
|
||||
from smbus2 import SMBus
|
||||
#import i2cpy
|
||||
#i2cset -y 1 0x11 0x11 0x42
|
||||
#set i2c address from 0x11 to 0x42
|
||||
|
||||
# ONLY FOR TESTING ON NON RPI
|
||||
#Device.pin_factory = gpiozero.pins.mock.MockFactory()
|
||||
|
||||
#relays = [LED(17), LED(27), LED(22), LED(23), LED(5), LED(6), LED(24), LED(25)]
|
||||
relayBoardMain = 0x42
|
||||
relayBoardSide = 0x43
|
||||
""" 0 1 2 3 """
|
||||
relayMasks = [0b0001, 0b0010, 0b0100, 0b1000] #probably ne rabim
|
||||
|
||||
bus = SMBus(1)
|
||||
|
||||
relMapping = {
|
||||
'service_down': 0,
|
||||
'service_up': 1,
|
||||
'down': 2,
|
||||
'up': 3
|
||||
}
|
||||
|
||||
currentState = {
|
||||
'glavni': 0b0000,
|
||||
'stranski': 0b0000
|
||||
}
|
||||
|
||||
async def msgRelayBoard(projSelect, command, state: bool):
|
||||
#i2cAddr = relayBoardMain if projSelect == 'glavni' else relayBoardSide
|
||||
#TODO this is not optimal, check for more crap
|
||||
|
||||
# register 0x10 za releje
|
||||
|
||||
I2CAddr = 0x42 #glavni
|
||||
if projSelect == 'stranski':
|
||||
I2CAddr += 0x1
|
||||
|
||||
maskShift = relMapping[command]
|
||||
mask = (1 << maskShift)
|
||||
if state:
|
||||
currentState[projSelect] = currentState[projSelect] | mask
|
||||
else:
|
||||
currentState[projSelect] = currentState[projSelect] & ~mask
|
||||
|
||||
bus.write_byte_data(I2CAddr, 0x10, currentState[projSelect])
|
||||
|
||||
print('testirovano jako')
|
||||
|
||||
|
||||
"""
|
||||
SrvDwn SrvUp OpDwn OpUp
|
||||
MAIN: 0x42 0001 0010 0100 1000
|
||||
SIDE: 0x43 0001 0010 0100 1000
|
||||
"""
|
||||
|
||||
|
||||
#old board
|
||||
"""
|
||||
MAIN: SrvDwn SrvUp OpDwn OpUp
|
||||
0 1 2 3
|
||||
SIDE: 4 5 6 7
|
||||
"""
|
||||
#dej like bolš to podukumentiraj or smth
|
||||
|
||||
async def task_command2relays(controlClient: aiomqtt.Client):
|
||||
|
||||
#relayCtrl = lambda cmd, relay: relays[relay].on() if cmd == 1 else relays[relay].off()
|
||||
#relayCtrl = lambda cmd, relay: [relays[r].on() if cmd == 1 and r == relay else relays[r].off for r in range(len(relays))]
|
||||
|
||||
relayCtrl = lambda x, y: print(x, y)
|
||||
await controlClient.subscribe("p01/projectors/+/lift/#")
|
||||
msgs = controlClient.messages
|
||||
async for mesg in msgs:
|
||||
mesg: aiomqtt.Message
|
||||
if mesg.topic.matches('p01/projectors/+/lift/move/#'):
|
||||
msgTopicArr = mesg.topic.value.split('/')
|
||||
state = mesg.payload.decode()
|
||||
print("Received: " + str(msgTopicArr) + " payload: [" + state + "]")
|
||||
#testCase = (msgTopicArr[2], msgTopicArr[4])
|
||||
projSel = msgTopicArr[2]
|
||||
if projSel != 'glavni' and projSel != 'stranski':
|
||||
continue
|
||||
command = msgTopicArr[5]
|
||||
await msgRelayBoard(projSel, command, state == '1')
|
||||
|
||||
#print("Pushing \'off\' to other relays to prevent conflicts")
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
|
||||
|
||||
async def main():
|
||||
async with aiomqtt.Client('localhost', 1883) as client:
|
||||
task_control = asyncio.create_task(task_command2relays(client))
|
||||
await asyncio.gather(task_control)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
19
tomlTest.py
19
tomlTest.py
|
@ -1,19 +0,0 @@
|
|||
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
|
|
@ -1,18 +0,0 @@
|
|||
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"
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 39 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue