Compare commits
16 Commits
4308dae03d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c70221723 | |||
|
|
9092f08481 | ||
|
|
e9ec94a5f8 | ||
|
|
fe744b2285 | ||
|
|
ffeee72652 | ||
|
|
fb6f10891d | ||
|
|
7501ef18a4 | ||
|
|
7776095180 | ||
|
|
578be1a1cf | ||
|
|
2a251d2700 | ||
|
|
f1d104a224 | ||
|
|
6c421a4ae1 | ||
|
|
23a2c1fb50 | ||
|
|
93034dd0ec | ||
|
|
d8c9a491d1 | ||
|
|
caf6232dfb |
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
ve_*
|
||||||
|
/server/scripts/docker-images/tagspace/tagspaces
|
||||||
|
/server/scripts/docker-images/tagspace/*.zip
|
||||||
|
*.img
|
||||||
|
/music
|
||||||
|
__pycache__
|
||||||
|
/roles/pi-squeezeserver/backup
|
||||||
|
venv*
|
||||||
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"python.linting.pylintEnabled": false,
|
||||||
|
"python.linting.enabled": true,
|
||||||
|
"python.linting.flake8Enabled": true,
|
||||||
|
"files.associations": {
|
||||||
|
"*.yaml": "home-assistant"
|
||||||
|
}
|
||||||
|
}
|
||||||
61
full.yml
Normal file
61
full.yml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
#- hosts: esszimmerradio
|
||||||
|
# roles:
|
||||||
|
# - pi-standard-setup
|
||||||
|
# - pi-squeezelite-custom
|
||||||
|
# - pi-shairport
|
||||||
|
# - pi-lirc
|
||||||
|
# - pi-sispmctl
|
||||||
|
#
|
||||||
|
|
||||||
|
- hosts: musikserverwohnzimmeroben
|
||||||
|
roles:
|
||||||
|
- pi-standard-setup
|
||||||
|
- pi-hifiberry-amp
|
||||||
|
- pi-squeezelite-custom
|
||||||
|
- pi-shairport
|
||||||
|
- pi-irserver
|
||||||
|
#- pi-dhtsensor
|
||||||
|
- pi-squeezeserver
|
||||||
|
|
||||||
|
- hosts: kitchenpi
|
||||||
|
roles:
|
||||||
|
- pi-standard-setup
|
||||||
|
- pi-hifiberry-amp
|
||||||
|
- pi-squeezelite-custom
|
||||||
|
- pi-shairport
|
||||||
|
- pi-lirc
|
||||||
|
- pi-dhtsensor
|
||||||
|
- pi-disable-onboard-bluetooth
|
||||||
|
- bluetooth-monitor
|
||||||
|
|
||||||
|
- hosts: bedroompi
|
||||||
|
roles:
|
||||||
|
- pi-standard-setup
|
||||||
|
- pi-squeezelite-custom
|
||||||
|
- pi-shairport
|
||||||
|
- pi-lirc
|
||||||
|
- pi-dhtsensor
|
||||||
|
- pi-disable-onboard-bluetooth
|
||||||
|
- bluetooth-monitor
|
||||||
|
|
||||||
|
- hosts: musicmouse
|
||||||
|
roles:
|
||||||
|
- pi-standard-setup
|
||||||
|
- pi-hifiberry-amp
|
||||||
|
- pi-musicmouse
|
||||||
|
- pi-squeezelite-custom
|
||||||
|
- pi-shairport
|
||||||
|
- pi-lirc
|
||||||
|
- bluetooth-monitor
|
||||||
|
|
||||||
|
#- hosts: octopi
|
||||||
|
# roles:
|
||||||
|
# - pi-dhtsensor
|
||||||
|
|
||||||
|
#- hosts: newrpi
|
||||||
|
# roles:
|
||||||
|
# - pi-standard-setup
|
||||||
|
# - pi-lirc
|
||||||
84
inventory.yml
Normal file
84
inventory.yml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
all:
|
||||||
|
hosts:
|
||||||
|
server:
|
||||||
|
ansible_host: home.bauer.tech
|
||||||
|
ansible_port: 22187
|
||||||
|
server2:
|
||||||
|
|
||||||
|
children:
|
||||||
|
iot:
|
||||||
|
hosts:
|
||||||
|
octopi:
|
||||||
|
sensor_room_name_ascii: prusaprinter
|
||||||
|
sensor_room_name: prusaprinter
|
||||||
|
dht_pin: 26
|
||||||
|
main_user: root
|
||||||
|
bedroompi:
|
||||||
|
squeezelite_name: BedroomPi
|
||||||
|
shairport_name: BedroomPi
|
||||||
|
alsa_card_name: Codec
|
||||||
|
sensor_room_name_ascii: schlafzimmer
|
||||||
|
sensor_room_name: Schlafzimmer
|
||||||
|
my_bt_monitor_watchdog_seconds: 600
|
||||||
|
my_btmonitor_restart_ble_interface: hci0
|
||||||
|
main_user: root
|
||||||
|
kitchenpi:
|
||||||
|
squeezelite_name: KitchenPi
|
||||||
|
shairport_name: KitchenPi
|
||||||
|
alsa_card_name: 0
|
||||||
|
sensor_room_name_ascii: kueche
|
||||||
|
sensor_room_name: Küche
|
||||||
|
hifiberry_overlay: hifiberry-amp
|
||||||
|
main_user: root
|
||||||
|
esszimmerradio: # oben, eltern
|
||||||
|
squeezelite_name: Esszimmer
|
||||||
|
shairport_name: _Oben_Esszimmer
|
||||||
|
#alsa_card_name: Device
|
||||||
|
squeezeserver: 192.168.178.100
|
||||||
|
configure_wifi: true
|
||||||
|
alsa_card_name: 1
|
||||||
|
main_user: root
|
||||||
|
musikserverwohnzimmeroben: # oben, eltern
|
||||||
|
squeezelite_name: Wohnzimmer
|
||||||
|
shairport_name: _Oben_Wohnzimmer
|
||||||
|
alsa_card_name: 0
|
||||||
|
squeezeserver: 192.168.178.100
|
||||||
|
sensor_room_name_ascii: wohnzimmeroben
|
||||||
|
sensor_room_name: WohnzimmerOben
|
||||||
|
hifiberry_overlay: hifiberry-dacplus
|
||||||
|
main_user: root
|
||||||
|
musicmouse:
|
||||||
|
squeezelite_name: MusicMouse
|
||||||
|
shairport_name: MusicMouse
|
||||||
|
alsa_card_name: 1
|
||||||
|
hifiberry_overlay: hifiberry-dacplus
|
||||||
|
sensor_room_name: Kinderzimmer
|
||||||
|
sensor_room_name_ascii: kinderzimmer
|
||||||
|
main_user: root
|
||||||
|
newrpi:
|
||||||
|
squeezelite_name: MyTestRaspberry
|
||||||
|
shairport_name: MyTestRaspberry
|
||||||
|
alsa_card_name: 0
|
||||||
|
sensor_room_name_ascii: testraum
|
||||||
|
sensor_room_name: Test Raum
|
||||||
|
heatingpi:
|
||||||
|
server:
|
||||||
|
sensor_room_name: Arbeitszimmer
|
||||||
|
sensor_room_name_ascii: arbeitszimmer
|
||||||
|
my_bt_monitor_watchdog_seconds: 600
|
||||||
|
my_btmonitor_restart_ble_interface: hci0
|
||||||
|
main_user: core
|
||||||
|
homeassistant:
|
||||||
|
sensor_room_name: Anschlussraum
|
||||||
|
sensor_room_name_ascii: anschlussraum
|
||||||
|
vars:
|
||||||
|
ansible_user: root
|
||||||
|
ansible_python_interpreter: /usr/bin/python3
|
||||||
|
squeezeserver: 192.168.178.80
|
||||||
|
router_ip: 192.168.178.1
|
||||||
|
home_assistant_url: https://ha.bauer.tech
|
||||||
|
home_assistant_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkM2QxYjAwYjkxZjY0MWVhYjA4YmZhMDYwYTg3YjRhNyIsImlhdCI6MTcwNDI3MDU5MSwiZXhwIjoyMDE5NjMwNTkxfQ.dzvejgEQd9hf-Yftzd7NkR5pv76GaLFczeOy-a2pa1o
|
||||||
|
configure_wifi: false
|
||||||
|
wifi_ssid: BauerWLAN
|
||||||
|
my_btmonitor_mqtt_username: my_btmonitor
|
||||||
|
my_btmonitor_mqtt_password: 8aBIAC14jaKKbla
|
||||||
BIN
lookup_plugins/__pycache__/keepass.cpython-38.pyc
Normal file
BIN
lookup_plugins/__pycache__/keepass.cpython-38.pyc
Normal file
Binary file not shown.
@@ -2,7 +2,7 @@
|
|||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.plugins.lookup import LookupBase
|
from ansible.plugins.lookup import LookupBase
|
||||||
from keepasshttplib import keepasshttplib, encrypter
|
from keepasshttplib import keepasshttplib, encrypter
|
||||||
|
import requests
|
||||||
|
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = """
|
||||||
lookup: keepass
|
lookup: keepass
|
||||||
@@ -39,12 +39,12 @@ class LookupModule(LookupBase):
|
|||||||
else:
|
else:
|
||||||
entry_path, entry_attr = terms[0], terms[1]
|
entry_path, entry_attr = terms[0], terms[1]
|
||||||
|
|
||||||
if not self._test_connection():
|
#if not self._test_connection():
|
||||||
raise AnsibleError('Keepass is closed!')
|
# raise AnsibleError('Keepass is closed!')
|
||||||
try:
|
try:
|
||||||
auth = self.k.get_credentials(entry_path)
|
auth = self.k.get_credentials(entry_path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AnsibleError('Keepass error obtaining entry {}: {}'.format(host_name, e))
|
raise AnsibleError('Keepass error obtaining entry {}: {}'.format(entry_path, e))
|
||||||
if auth:
|
if auth:
|
||||||
if entry_attr not in ('username', 'user', 'pass', 'passwd', 'password'):
|
if entry_attr not in ('username', 'user', 'pass', 'passwd', 'password'):
|
||||||
raise AnsibleError("Keepass wrong entry")
|
raise AnsibleError("Keepass wrong entry")
|
||||||
8
newrpi-provisioning.yml
Normal file
8
newrpi-provisioning.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
- hosts: musicmouse
|
||||||
|
roles:
|
||||||
|
- pi-standard-setup
|
||||||
|
- pi-hifiberry-amp
|
||||||
|
- pi-squeezelite
|
||||||
|
- pi-shairport
|
||||||
|
- pi-lirc
|
||||||
3
octopisetup.yml
Normal file
3
octopisetup.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
- hosts: octopi
|
||||||
|
roles:
|
||||||
|
- pi-dhtsensor
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
# Run with
|
|
||||||
# ansible-playbook 01-download-and-prepare-raspi-image.yml
|
|
||||||
---
|
|
||||||
|
|
||||||
- hosts: 127.0.0.1
|
|
||||||
connection: local
|
|
||||||
gather_facts: false
|
|
||||||
vars:
|
|
||||||
target_folder: "/media/martin/data_linux/tmp/"
|
|
||||||
vars_prompt:
|
|
||||||
- name: "ansible_become_pass"
|
|
||||||
prompt: "Sudo password to mount raspi image"
|
|
||||||
- name: "new_hostname"
|
|
||||||
prompt: "New hostname for the PI"
|
|
||||||
private: no
|
|
||||||
tasks:
|
|
||||||
# --- Preparation ---
|
|
||||||
- name: Download Raspian image
|
|
||||||
get_url:
|
|
||||||
url: "https://downloads.raspberrypi.org/raspbian_lite_latest"
|
|
||||||
dest: "{{target_folder}}/raspian_lite_latest.zip"
|
|
||||||
- name: Unpack Image
|
|
||||||
unarchive:
|
|
||||||
src: "{{target_folder}}/raspian_lite_latest.zip"
|
|
||||||
dest: "{{target_folder}}"
|
|
||||||
creates: "{{target_folder}}/*raspbian*.img"
|
|
||||||
- name: Make Folders to mount to
|
|
||||||
file:
|
|
||||||
path: "{{item}}"
|
|
||||||
state: directory
|
|
||||||
with_items:
|
|
||||||
- "{{target_folder}}/mounted_raspi_image"
|
|
||||||
- "{{target_folder}}/mounted_raspi_image/boot"
|
|
||||||
- "{{target_folder}}/mounted_raspi_image/system"
|
|
||||||
- name: Setup Loopback
|
|
||||||
become: true
|
|
||||||
shell:
|
|
||||||
cmd: "losetup -P /dev/loop42 {{target_folder}}/*raspbian*.img"
|
|
||||||
creates: "/dev/loop42p1"
|
|
||||||
- name: Mount Boot Partition
|
|
||||||
become: true
|
|
||||||
shell:
|
|
||||||
warn: false
|
|
||||||
cmd: "mount /dev/loop42p1 {{target_folder}}/mounted_raspi_image/boot"
|
|
||||||
creates: "{{target_folder}}/mounted_raspi_image/boot/kernel.img"
|
|
||||||
- name: Mount System Partition
|
|
||||||
become: true
|
|
||||||
shell:
|
|
||||||
warn: false
|
|
||||||
cmd: "mount /dev/loop42p2 {{target_folder}}/mounted_raspi_image/system"
|
|
||||||
creates: "{{target_folder}}/mounted_raspi_image/system/bin"
|
|
||||||
# --- Actual work ---
|
|
||||||
- name: "Add SSH File to boot partition to allow for first remote login"
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{target_folder}}/mounted_raspi_image/boot/ssh"
|
|
||||||
state: touch
|
|
||||||
- name: "Writing new hostname to /etc/hostname"
|
|
||||||
become: true
|
|
||||||
copy:
|
|
||||||
content: "{{new_hostname}}"
|
|
||||||
dest: "{{target_folder}}/mounted_raspi_image/system/etc/hostname"
|
|
||||||
# --- Wind-down
|
|
||||||
- name: Unmount System Partition
|
|
||||||
become: true
|
|
||||||
shell:
|
|
||||||
warn: false
|
|
||||||
cmd: "umount /dev/loop42p2"
|
|
||||||
removes: "{{target_folder}}/mounted_raspi_image/system/bin"
|
|
||||||
- name: Unmount Boot Partition
|
|
||||||
become: true
|
|
||||||
shell:
|
|
||||||
warn: false
|
|
||||||
cmd: "umount /dev/loop42p1"
|
|
||||||
removes: "{{target_folder}}/mounted_raspi_image/boot/kernel.img"
|
|
||||||
- name: Tear down loop device
|
|
||||||
become: true
|
|
||||||
shell:
|
|
||||||
cmd: "losetup -d /dev/loop42"
|
|
||||||
removes: "/dev/loop42p1"
|
|
||||||
- name: Remove folders
|
|
||||||
file:
|
|
||||||
path: "{{item}}"
|
|
||||||
state: absent
|
|
||||||
with_items:
|
|
||||||
- "{{target_folder}}/mounted_raspi_image"
|
|
||||||
- "{{target_folder}}/mounted_raspi_image/boot"
|
|
||||||
- "{{target_folder}}/mounted_raspi_image/system"
|
|
||||||
- "{{target_folder}}/raspian_lite_latest.zip"
|
|
||||||
- name: Final Image
|
|
||||||
debug:
|
|
||||||
msg: |
|
|
||||||
The prepared image is ready at {{target_folder}}.
|
|
||||||
Copy it to sdcard with
|
|
||||||
dd bs=4M status=progress if=the_image of=/dev/your/sdcard
|
|
||||||
use e.g. /dev/sdb not /dev/sdb1 !
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
# Run with
|
|
||||||
# ansible-playbook -i raspberrypi, 02-provision_new_pi.yml
|
|
||||||
# where "raspberrypi" is the hostname of the pi
|
|
||||||
---
|
|
||||||
|
|
||||||
- hosts: kitchenpi
|
|
||||||
gather_facts: false
|
|
||||||
vars:
|
|
||||||
timezone: "Europe/Berlin"
|
|
||||||
wifi_country: "DE"
|
|
||||||
wifi_ssid: "" # put SSID here to configure wifi
|
|
||||||
wifi_pass_url: "bauer_wifi" # has to be in keepass with url "wifi_pass_url"
|
|
||||||
ansible_ssh_pass: raspberry
|
|
||||||
ansible_become: yes
|
|
||||||
ansible_become_password: raspberry
|
|
||||||
new_hostname: "" # set this to change the hostname
|
|
||||||
vars_prompt:
|
|
||||||
- name: ansible_user
|
|
||||||
prompt: "User to connect with, put in 'pi' here if you connect the first time, else leave empty"
|
|
||||||
default: root
|
|
||||||
tasks:
|
|
||||||
- name: Do apt update/upgrade
|
|
||||||
apt: upgrade=yes update_cache=yes cache_valid_time=7200
|
|
||||||
- name: Detect Raspi Model
|
|
||||||
slurp: src=/sys/firmware/devicetree/base/model
|
|
||||||
register: raspberry_model
|
|
||||||
- name: Show Raspi Model
|
|
||||||
debug: msg={{ raspberry_model.content | b64decode }}
|
|
||||||
- name: Add authorized SSH key to root account
|
|
||||||
authorized_key:
|
|
||||||
user: root
|
|
||||||
key: "{{ lookup('file', '../public_keys/martin_laptop.pub') }}"
|
|
||||||
state: present
|
|
||||||
- name: Activate root login with key
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/ssh/sshd_config
|
|
||||||
regexp: "^#?PermitRootLogin"
|
|
||||||
line: "PermitRootLogin prohibit-password"
|
|
||||||
notify: restart sshd
|
|
||||||
- name: Deactive SSH accepting locale vars (leads to warnings)
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/ssh/sshd_config
|
|
||||||
regexp: "^#?AcceptEnv LANG LC_*"
|
|
||||||
line: "#AcceptEnv LANG LC_*"
|
|
||||||
notify: restart sshd
|
|
||||||
- name: Get hostname
|
|
||||||
command: "raspi-config nonint get_hostname"
|
|
||||||
register: pi_hostname
|
|
||||||
changed_when: False
|
|
||||||
- name: Change hostname to {{ new_hostname }}
|
|
||||||
command: "raspi-config nonint do_hostname {{ new_hostname }}"
|
|
||||||
when: new_hostname | bool and pi_hostname.stdout != new_hostname
|
|
||||||
- name: set boot mode to CLI
|
|
||||||
command: "raspi-config nonint do_boot_behaviour B1"
|
|
||||||
#I2 Change Timezone
|
|
||||||
- name: Change timezone
|
|
||||||
command: "raspi-config nonint do_change_timezone {{ timezone }}"
|
|
||||||
- name: Change locale
|
|
||||||
command: "raspi-config nonint do_change_locale en_US.UTF-8"
|
|
||||||
- name: Change password of default pi account
|
|
||||||
user:
|
|
||||||
name: pi
|
|
||||||
update_password: always
|
|
||||||
password: "{{ lookup('keepass', 'default_rpi_password') | password_hash('sha512') }}"
|
|
||||||
- name: Install Packages (vim, git, basic python stuff)
|
|
||||||
apt:
|
|
||||||
name:
|
|
||||||
- vim
|
|
||||||
- git
|
|
||||||
- python3
|
|
||||||
- python3-pip
|
|
||||||
- python3-wheel
|
|
||||||
cache_valid_time: 7200
|
|
||||||
state: present
|
|
||||||
- name: Copy vim config
|
|
||||||
copy: src=../configs/vimrc dest=/root/.vimrc
|
|
||||||
- name: Copy git config
|
|
||||||
copy: src=../configs/gitconfig dest=/root/.gitconfig
|
|
||||||
# Wifi
|
|
||||||
- name: Get WiFi country
|
|
||||||
command: "raspi-config nonint get_wifi_country"
|
|
||||||
register: wifi_country
|
|
||||||
changed_when: False
|
|
||||||
ignore_errors: yes #to avoid error when WiFi is not present
|
|
||||||
- name: Change WiFi country
|
|
||||||
command: "raspi-config nonint do_wifi_country {{ wifi_country }}"
|
|
||||||
- name: Set WiFi credentials
|
|
||||||
command: "raspi-config nonint do_wifi_ssid_passphrase {{ wifi_ssid }} {{ lookup('keepass', wifi_pass_url) }}"
|
|
||||||
when: wifi_ssid | bool
|
|
||||||
# Message of the day
|
|
||||||
- name: Set Message of the day
|
|
||||||
copy: src=configs/motd/{{ inventory_hostname }} dest=/etc/motd
|
|
||||||
#- name: Remove motd tail
|
|
||||||
# copy: dest=/etc/motd.
|
|
||||||
# LED off script
|
|
||||||
- name: Copy led off script
|
|
||||||
copy: src=configs/raspi-leds-off.sh dest=/usr/sbin/raspi-leds-off.sh mode="u+rwx"
|
|
||||||
- name: Copy led off service
|
|
||||||
copy: src=configs/raspi-leds-off.service dest=/lib/systemd/system/
|
|
||||||
- name: Activate led off servic
|
|
||||||
systemd: name=raspi-leds-off state=restarted enabled=yes daemon_reload=yes
|
|
||||||
|
|
||||||
handlers:
|
|
||||||
- name: restart sshd
|
|
||||||
service:
|
|
||||||
name: sshd
|
|
||||||
state: restarted
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Install instructions taken from
|
|
||||||
# https://github.com/mikebrady/shairport-sync/blob/master/INSTALL.md
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
- hosts: kitchenpi
|
|
||||||
gather_facts: false
|
|
||||||
vars:
|
|
||||||
shairport_sync_version: "3.3.5"
|
|
||||||
remote_user: root
|
|
||||||
tasks:
|
|
||||||
- name: Apt install dependencies
|
|
||||||
apt:
|
|
||||||
cache_valid_time: 7200
|
|
||||||
state: present
|
|
||||||
name:
|
|
||||||
- build-essential
|
|
||||||
- git
|
|
||||||
- xmltoman
|
|
||||||
- autoconf
|
|
||||||
- automake
|
|
||||||
- libtool
|
|
||||||
- libpopt-dev
|
|
||||||
- libconfig-dev
|
|
||||||
- libasound2-dev
|
|
||||||
- avahi-daemon
|
|
||||||
- libavahi-client-dev
|
|
||||||
- libssl-dev
|
|
||||||
- libsoxr-dev
|
|
||||||
- name: Build and Install Shairport sync (may take a while)
|
|
||||||
script: "scripts/build-shairport-sync.sh ${shairport_sync_version}"
|
|
||||||
args:
|
|
||||||
creates: /usr/local/bin/shairport-sync
|
|
||||||
- name: Copy config
|
|
||||||
template: src=configs/shairport-sync.conf dest=/etc/shairport-sync.conf
|
|
||||||
- name: Sync alsa config
|
|
||||||
template: src=configs/asound.conf dest=/etc/asound.conf
|
|
||||||
- name: Modify service file to run as root
|
|
||||||
lineinfile:
|
|
||||||
path: /lib/systemd/system/shairport-sync.service
|
|
||||||
regexp: "^#?User="
|
|
||||||
line: "User=root"
|
|
||||||
- name: Restart shairport-sync
|
|
||||||
systemd: name=shairport-sync state=restarted enabled=yes daemon_reload=yes
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
- hosts: kitchenpi
|
|
||||||
gather_facts: false
|
|
||||||
remote_user: root
|
|
||||||
tasks:
|
|
||||||
- name: Apt install squeezelite package
|
|
||||||
apt: name=squeezelite cache_valid_time=7200 state=present
|
|
||||||
- name: Install config file
|
|
||||||
template: src=configs/squeezelite.cfg dest=/etc/default/squeezelite
|
|
||||||
- name: Sync alsa config
|
|
||||||
template: src=configs/asound.conf dest=/etc/asound.conf
|
|
||||||
- name: Restart squeezelite
|
|
||||||
systemd: name=squeezelite state=restarted enabled=yes daemon_reload=yes
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
# lirc needs to be custom compiled on this kernel
|
|
||||||
# https://gist.github.com/billpatrianakos/cb72e984d4730043fe79cbe5fc8f7941
|
|
||||||
- hosts: kitchenpi
|
|
||||||
gather_facts: false
|
|
||||||
remote_user: root
|
|
||||||
tasks:
|
|
||||||
#- name: Apt install lirc package
|
|
||||||
# apt: name=lirc cache_valid_time=7200 state=present
|
|
||||||
# ignore_errors: yes
|
|
||||||
- name: Install config file lirc_options.conf
|
|
||||||
copy: src=configs/lirc/lirc_options.conf dest=/etc/lirc/lirc_options.conf
|
|
||||||
- name: Install config file lircd.conf
|
|
||||||
copy: src=configs/lirc/lircd.conf dest=/etc/lirc/lircd.conf
|
|
||||||
- name: Install remote file
|
|
||||||
copy: src=configs/lirc/hauppauge.conf dest=/etc/lirc/hauppauge.conf
|
|
||||||
- name: create temporary directory
|
|
||||||
tempfile:
|
|
||||||
state: directory
|
|
||||||
suffix: temp
|
|
||||||
register: tempdir
|
|
||||||
- name: Copy over lirc customly compiled lirc packages
|
|
||||||
copy:
|
|
||||||
src: configs/lirc/debs/
|
|
||||||
dest: "{{ tempdir.path }}"
|
|
||||||
when: tempdir.path is defined
|
|
||||||
- name: Install custom lirc package 1
|
|
||||||
apt:
|
|
||||||
deb: "{{ tempdir.path }}/liblirc0_0.10.1-5.2_armhf.deb"
|
|
||||||
when: tempdir.path is defined
|
|
||||||
- name: Install custom lirc package 2
|
|
||||||
apt:
|
|
||||||
deb: "{{ tempdir.path }}/liblircclient0_0.10.1-5.2_armhf.deb"
|
|
||||||
when: tempdir.path is defined
|
|
||||||
- name: Install custom lirc package 3
|
|
||||||
apt:
|
|
||||||
deb: "{{ tempdir.path }}/lirc_0.10.1-5.2_armhf.deb"
|
|
||||||
when: tempdir.path is defined
|
|
||||||
- name: Activate overlay in boot config
|
|
||||||
lineinfile:
|
|
||||||
path: /boot/config.txt
|
|
||||||
regexp: "^#?dtoverlay=gpio-ir"
|
|
||||||
line: "dtoverlay=gpio-ir,gpio_pin=17"
|
|
||||||
register: boot_overlay
|
|
||||||
- name: Restart lircd
|
|
||||||
systemd: name=lircd state=restarted enabled=yes daemon_reload=yes
|
|
||||||
- name: Reboot if boot overlay changed
|
|
||||||
reboot:
|
|
||||||
when: boot_overlay.changed
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
- hosts: newrpi
|
|
||||||
gather_facts: false
|
|
||||||
remote_user: root
|
|
||||||
tasks:
|
|
||||||
- name: Deactivate normal audio
|
|
||||||
lineinfile:
|
|
||||||
path: /boot/config.txt
|
|
||||||
regexp: "^#?dtparam=audio=on"
|
|
||||||
line: "#dtparam=audio=on"
|
|
||||||
register: boot_overlay1
|
|
||||||
- name: Activate Hifiberry
|
|
||||||
lineinfile:
|
|
||||||
path: /boot/config.txt
|
|
||||||
regexp: "^#?dtoverlay=hifiberry-amp"
|
|
||||||
line: "dtoverlay=hifiberry-amp"
|
|
||||||
register: boot_overlay2
|
|
||||||
#- name: Reboot if boot overlay changed
|
|
||||||
# reboot:
|
|
||||||
# when: boot_overlay1.changed or boot_overlay2.changed
|
|
||||||
|
|
||||||
|
|
||||||
## State in /boot/config.txt
|
|
||||||
# dtoverlay=hifiberry-amp
|
|
||||||
# # remove old:
|
|
||||||
# #dtparam=audio=on
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
- hosts: kitchenpi
|
|
||||||
gather_facts: false
|
|
||||||
remote_user: root
|
|
||||||
tasks:
|
|
||||||
- name: apt install libgpiod2
|
|
||||||
apt: name=libgpiod2 cache_valid_time=7200 state=present
|
|
||||||
- name: pip install adafruit-circuitpython-dht
|
|
||||||
pip:
|
|
||||||
name: adafruit-circuitpython-dht
|
|
||||||
executable: pip3
|
|
||||||
- name: Install script config
|
|
||||||
template: src=configs/dht22_sensing.json dest=/etc/dht22_sensing.json
|
|
||||||
- name: Install script
|
|
||||||
copy: src=configs/dht22_sensing.py dest=/usr/bin/dht22_sensing owner=root mode=u+rwx
|
|
||||||
- name: Install systemd service file
|
|
||||||
copy: src=configs/dht22_sensing.service dest=/lib/systemd/system/
|
|
||||||
- name: Add script to autostart and start now
|
|
||||||
systemd: name=dht22_sensing state=restarted enabled=yes daemon_reload=yes
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
include "hauppauge.conf"
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
all:
|
|
||||||
hosts:
|
|
||||||
server:
|
|
||||||
ansible_host: home.bauer.tech
|
|
||||||
ansible_port: 22187
|
|
||||||
|
|
||||||
children:
|
|
||||||
iot:
|
|
||||||
hosts:
|
|
||||||
newrpi:
|
|
||||||
squeezelite_name: MyTestRaspberry
|
|
||||||
shairport_name: MyTestRaspberry
|
|
||||||
alsa_card_name: 0
|
|
||||||
sensor_room_name_ascii: testraum
|
|
||||||
sensor_room_name: Test Raum
|
|
||||||
heatingpi:
|
|
||||||
bedroompi:
|
|
||||||
kitchenpi:
|
|
||||||
squeezelite_name: KitchenPi
|
|
||||||
shairport_name: KitchenPi
|
|
||||||
alsa_card_name: 0
|
|
||||||
sensor_room_name_ascii: kueche
|
|
||||||
sensor_room_name: Küche
|
|
||||||
vars:
|
|
||||||
ansible_python_interpreter: /usr/bin/python3
|
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
/etc/modules
|
|
||||||
-> bedroompi: snd-bcm2835
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import requests
|
|
||||||
|
|
||||||
key = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIxNjkxMWIzZmQ4ZWU0NDI0OTg0MjA0ZDllMDhkNGRlMCIsImlhdCI6MTU3ODE3MDU5MSwiZXhwIjoxODkzNTMwNTkxfQ.i7CdXEZy9DV9KPHAl-msK0rOfIUlPYo4zwwJ4UGhXuc"
|
|
||||||
url = "https://ha.bauer.tech"
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
'x-ha-access': key,
|
|
||||||
'Authorization': "Bearer {}".format(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
apiurl = url + "/api/states/sensor.schlafzimmer_temperatur"
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"state": "19",
|
|
||||||
"attributes": {
|
|
||||||
"device_class": "temperature",
|
|
||||||
"friendly_name": "Schlafzimmer Temperatur",
|
|
||||||
"unit_of_measurement": "°C"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r = requests.post(apiurl, json=data, headers=headers)
|
|
||||||
|
|
||||||
print(r)
|
|
||||||
3
roles/better-shell-env/tasks/files/dot.fish
Normal file
3
roles/better-shell-env/tasks/files/dot.fish
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
function dot -w git -d "Manages dotfiles"
|
||||||
|
git --git-dir=$HOME/.dot --work-tree=$HOME $argv
|
||||||
|
end
|
||||||
47
roles/better-shell-env/tasks/main.yml
Normal file
47
roles/better-shell-env/tasks/main.yml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
#
|
||||||
|
- name: Install packages
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- bat
|
||||||
|
- broot
|
||||||
|
- duf
|
||||||
|
- fd-find
|
||||||
|
- fish
|
||||||
|
- fzf
|
||||||
|
- git
|
||||||
|
- glances
|
||||||
|
- lsd
|
||||||
|
- neovim
|
||||||
|
- ripgrep
|
||||||
|
- tmux
|
||||||
|
- zoxide
|
||||||
|
|
||||||
|
# TODO: Dust, btm
|
||||||
|
|
||||||
|
block:
|
||||||
|
become: true
|
||||||
|
become_user: {{main_user}}
|
||||||
|
- name: Create bin folder
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: ~/bin
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
# Oh-my-fish
|
||||||
|
- name: get oh-my-fish repo
|
||||||
|
git:
|
||||||
|
repo: 'https://github.com/oh-my-fish/oh-my-fish.git'
|
||||||
|
dest: ~/.local/share/git/oh-my-fish
|
||||||
|
- name: install oh-my-fish
|
||||||
|
shell:
|
||||||
|
cmd: "bin/install --offline --noninteractive"
|
||||||
|
executable: /usr/bin/fish
|
||||||
|
chdir: ~/.local/share/git/oh-my-fish
|
||||||
|
creates:
|
||||||
|
- ~/.local/share/omf
|
||||||
|
- ~/.config/omf
|
||||||
|
- copy:
|
||||||
|
content: "bobthefish"
|
||||||
|
dst: "~/.config/omf/theme"
|
||||||
|
|
||||||
131
roles/bluetooth-monitor/files/filter.cpp
Normal file
131
roles/bluetooth-monitor/files/filter.cpp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using real_t = double;
|
||||||
|
|
||||||
|
static constexpr real_t SPIKE_THRESHOLD = 1.0f; // Threshold for spike detection
|
||||||
|
static constexpr int NUM_READINGS = 12; // Number of readings to keep track of
|
||||||
|
|
||||||
|
|
||||||
|
class FilteredDistance {
|
||||||
|
public:
|
||||||
|
FilteredDistance(real_t minCutoff = 1e-1f, real_t beta = 1e-3, real_t dcutoff = 5e-3);
|
||||||
|
void addMeasurement(real_t dist, real_t time_now_in_seconds);
|
||||||
|
const real_t getMedianDistance() const;
|
||||||
|
const real_t getDistance() const;
|
||||||
|
const real_t getVariance() const;
|
||||||
|
|
||||||
|
bool hasValue() const { return lastTime != 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
real_t minCutoff;
|
||||||
|
real_t beta;
|
||||||
|
real_t dcutoff;
|
||||||
|
real_t x, dx;
|
||||||
|
real_t lastDist;
|
||||||
|
real_t lastTime;
|
||||||
|
|
||||||
|
real_t getAlpha(real_t cutoff, real_t dT);
|
||||||
|
|
||||||
|
real_t readings[NUM_READINGS]; // Array to store readings
|
||||||
|
int readIndex; // Current position in the array
|
||||||
|
real_t total; // Total of the readings
|
||||||
|
real_t totalSquared; // Total of the squared readings
|
||||||
|
|
||||||
|
void initSpike(real_t dist);
|
||||||
|
real_t removeSpike(real_t dist);
|
||||||
|
};
|
||||||
|
|
||||||
|
FilteredDistance::FilteredDistance(real_t minCutoff, real_t beta, real_t dcutoff)
|
||||||
|
: minCutoff(minCutoff), beta(beta), dcutoff(dcutoff), x(0), dx(0), lastDist(0), lastTime(-1), total(0), totalSquared(0), readIndex(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilteredDistance::initSpike(real_t dist) {
|
||||||
|
for (size_t i = 0; i < NUM_READINGS; i++) {
|
||||||
|
readings[i] = dist;
|
||||||
|
}
|
||||||
|
total = dist * NUM_READINGS;
|
||||||
|
totalSquared = dist * dist * NUM_READINGS; // Initialize sum of squared distances
|
||||||
|
}
|
||||||
|
|
||||||
|
real_t FilteredDistance::removeSpike(real_t dist) {
|
||||||
|
total -= readings[readIndex]; // Subtract the last reading
|
||||||
|
totalSquared -= readings[readIndex] * readings[readIndex]; // Subtract the square of the last reading
|
||||||
|
|
||||||
|
readings[readIndex] = dist; // Read the sensor
|
||||||
|
total += readings[readIndex]; // Add the reading to the total
|
||||||
|
totalSquared += readings[readIndex] * readings[readIndex]; // Add the square of the reading
|
||||||
|
|
||||||
|
readIndex = (readIndex + 1) % NUM_READINGS; // Advance to the next position in the array
|
||||||
|
|
||||||
|
auto average = total / static_cast<real_t>(NUM_READINGS); // Calculate the average
|
||||||
|
|
||||||
|
if (std::fabs(dist - average) > SPIKE_THRESHOLD)
|
||||||
|
return average; // Spike detected, use the average as the filtered value
|
||||||
|
|
||||||
|
return dist; // No spike, return the new value
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilteredDistance::addMeasurement(real_t dist, real_t time_now_in_seconds) {
|
||||||
|
const bool initialized = lastTime >= 0;
|
||||||
|
const real_t elapsed = time_now_in_seconds - lastTime;
|
||||||
|
lastTime = time_now_in_seconds;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
x = dist; // Set initial filter state to the first reading
|
||||||
|
dx = 0; // Initial derivative is unknown, so we set it to zero
|
||||||
|
lastDist = dist;
|
||||||
|
initSpike(dist);
|
||||||
|
} else {
|
||||||
|
real_t dT = std::max(elapsed, real_t(0.05)); // Convert microseconds to seconds, enforce a minimum dT
|
||||||
|
const real_t alpha = getAlpha(minCutoff, dT);
|
||||||
|
const real_t dAlpha = getAlpha(dcutoff, dT);
|
||||||
|
dist = removeSpike(dist);
|
||||||
|
x += alpha * (dist - x);
|
||||||
|
dx = dAlpha * ((dist - lastDist) / dT);
|
||||||
|
lastDist = x + beta * dx;
|
||||||
|
std::cout << "alpha=" << alpha <<
|
||||||
|
" dAlpha=" << dAlpha <<
|
||||||
|
" dist=" << dist <<
|
||||||
|
" x=" << x <<
|
||||||
|
" dx=" << dx <<
|
||||||
|
" lastDist=" << lastDist <<
|
||||||
|
std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const real_t FilteredDistance::getDistance() const {
|
||||||
|
return lastDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
real_t FilteredDistance::getAlpha(real_t cutoff, real_t dT) {
|
||||||
|
real_t tau = 1.0f / (2 * M_PI * cutoff);
|
||||||
|
return 1.0f / (1.0f + tau / dT);
|
||||||
|
}
|
||||||
|
|
||||||
|
const real_t FilteredDistance::getVariance() const {
|
||||||
|
auto mean = total / static_cast<real_t>(NUM_READINGS);
|
||||||
|
auto meanOfSquares = totalSquared / static_cast<real_t>(NUM_READINGS);
|
||||||
|
auto variance = meanOfSquares - (mean * mean); // Variance formula: E(X^2) - (E(X))^2
|
||||||
|
if (variance < 0.0f) return 0.0f;
|
||||||
|
return variance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char**argv)
|
||||||
|
{
|
||||||
|
FilteredDistance f;
|
||||||
|
std::vector<real_t> values = {1.5, 2.9, 5.3, 15.1, 1.5, 2.5, 1.5, 2.9, 5.3, 15.1};
|
||||||
|
|
||||||
|
real_t time = 0.0;
|
||||||
|
//std::cout << " result_cpp = [";
|
||||||
|
for(int i=0; i < 1; ++i)
|
||||||
|
for(auto value : values) {
|
||||||
|
f.addMeasurement(value, time);
|
||||||
|
time += 1.0;
|
||||||
|
//std::cout << f.getDistance() << ", ";
|
||||||
|
}
|
||||||
|
//std::cout << "]" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
91
roles/bluetooth-monitor/files/filter.py
Normal file
91
roles/bluetooth-monitor/files/filter.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#from time import time
|
||||||
|
import math
|
||||||
|
from scipy import signal
|
||||||
|
|
||||||
|
# Taken from ESPresense C++ code
|
||||||
|
class FilteredDistance:
|
||||||
|
NUM_READINGS = 100
|
||||||
|
SPIKE_THRESHOLD = 1.0
|
||||||
|
|
||||||
|
def __init__(self, min_cutoff : float = 1e-1, beta : float = 1e-3, dcutoff : float = 5e-3):
|
||||||
|
self.min_cutoff = min_cutoff
|
||||||
|
self.beta = beta
|
||||||
|
self.dcutoff = dcutoff
|
||||||
|
self.x = 0
|
||||||
|
self.dx = 0
|
||||||
|
self.last_dist = 0
|
||||||
|
self.last_time = -1.0
|
||||||
|
self.total = 0
|
||||||
|
self.read_index = 0
|
||||||
|
self.readings = []
|
||||||
|
|
||||||
|
def _init_spike(self, dist : float):
|
||||||
|
self.readings = [dist] * self.NUM_READINGS
|
||||||
|
self.total = sum(self.readings)
|
||||||
|
|
||||||
|
def _remove_spike(self, dist: float):
|
||||||
|
self.total -= self.readings[self.read_index]
|
||||||
|
|
||||||
|
self.readings[self.read_index] = dist
|
||||||
|
|
||||||
|
self.total += dist
|
||||||
|
|
||||||
|
self.read_index = (self.read_index + 1) % self.NUM_READINGS
|
||||||
|
average = self.total / self.NUM_READINGS
|
||||||
|
if abs(dist - average) > self.SPIKE_THRESHOLD:
|
||||||
|
return average # spike detected
|
||||||
|
else:
|
||||||
|
return dist
|
||||||
|
|
||||||
|
def _get_alpha(self, cutoff : float, dT : float):
|
||||||
|
tau = 1 / (2 * math.pi * cutoff)
|
||||||
|
return 1 / (1 + tau / dT)
|
||||||
|
|
||||||
|
def add_measurement(self, dist : float, time_now_in_seconds: float):
|
||||||
|
initialized = (self.last_time >= 0.0)
|
||||||
|
elapsed = time_now_in_seconds - self.last_time
|
||||||
|
self.last_time = time_now_in_seconds
|
||||||
|
if not initialized:
|
||||||
|
self.x = dist
|
||||||
|
self.dx = 0
|
||||||
|
self.last_dist = dist
|
||||||
|
self._init_spike(dist)
|
||||||
|
else:
|
||||||
|
dT = max(elapsed, 0.05)
|
||||||
|
alpha = self._get_alpha(self.min_cutoff, dT)
|
||||||
|
d_alpha = self._get_alpha(self.dcutoff, dT)
|
||||||
|
dist = self._remove_spike(dist)
|
||||||
|
self.x += alpha * (dist - self.x)
|
||||||
|
self.dx = d_alpha * ((dist - self.last_dist) / dT)
|
||||||
|
self.last_dist = self.x + self.beta * self.dx
|
||||||
|
#print(f"{alpha=} {d_alpha=} {dist=} {self.x=} {self.dx=} {self.last_dist=}")
|
||||||
|
|
||||||
|
def get_distance(self):
|
||||||
|
return self.last_dist
|
||||||
|
|
||||||
|
def run_test(times, values, **kwargs):
|
||||||
|
f = FilteredDistance(**kwargs)
|
||||||
|
result = []
|
||||||
|
for t, value in zip(times, values):
|
||||||
|
f.add_measurement(value, t)
|
||||||
|
result.append(f.get_distance())
|
||||||
|
return result
|
||||||
|
|
||||||
|
def smooth(y, box_pts):
|
||||||
|
box = np.ones(box_pts)/box_pts
|
||||||
|
y_smooth = np.convolve(y, box, mode='same')
|
||||||
|
return y_smooth
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import numpy as np
|
||||||
|
values = np.array([1] * 20 + [2, 4, 6, 7, 10, 16, 10, 13, 16, 24, 13] + [1] * 20 )
|
||||||
|
|
||||||
|
times = np.arange(0, len(values)) * 10
|
||||||
|
result_default = run_test(times, values)
|
||||||
|
result_beta1 = smooth(values, 6)
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
plt.plot(times, values, label="raw")
|
||||||
|
#plt.plot(times, result_default, marker="o", label="filtered")
|
||||||
|
plt.plot(times, result_beta1, marker='x', label="altered")
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
BIN
roles/bluetooth-monitor/files/filtered
Executable file
BIN
roles/bluetooth-monitor/files/filtered
Executable file
Binary file not shown.
11
roles/bluetooth-monitor/files/my_btmonitor.service
Normal file
11
roles/bluetooth-monitor/files/my_btmonitor.service
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=My Bluetooth monitor
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
ExecStart=/usr/bin/my_btmonitor
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
11
roles/bluetooth-monitor/other/Dockerfile
Normal file
11
roles/bluetooth-monitor/other/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM python:3
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY requirements.txt ./
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY bt_monitor_server.py .
|
||||||
|
COPY training_data.csv .
|
||||||
|
|
||||||
|
CMD [ "python", "./bt_monitor_server.py" ]
|
||||||
95
roles/bluetooth-monitor/other/analysis.py
Normal file
95
roles/bluetooth-monitor/other/analysis.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
from copy import copy
|
||||||
|
from sklearn.model_selection import cross_val_score
|
||||||
|
from sklearn import svm
|
||||||
|
from sklearn.neural_network import MLPClassifier
|
||||||
|
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from sklearn.model_selection import train_test_split
|
||||||
|
|
||||||
|
def load_measurements(csv_file: Path):
|
||||||
|
def cleanup_column_name(col_name: str):
|
||||||
|
clean_name = col_name.replace('#', '').strip()
|
||||||
|
if clean_name == 'room':
|
||||||
|
return 'tracker'
|
||||||
|
return clean_name
|
||||||
|
|
||||||
|
df = pd.read_csv(str(csv_file))
|
||||||
|
|
||||||
|
# String cleanup in column names and room names
|
||||||
|
df = df.rename(columns=cleanup_column_name)
|
||||||
|
df.applymap(lambda x: x.strip() if isinstance(x, str) else x)
|
||||||
|
|
||||||
|
df['tracker'] = df['tracker'].astype("category")
|
||||||
|
df['real_room'] = df['real_room'].astype("category")
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
FAR_AWAY_FEATURE_VALUE = 1
|
||||||
|
def get_feature_value(rssi, tx_power):
|
||||||
|
MIN_RSSI = -90
|
||||||
|
MAX_TRANSFORMED_RSSI = 40
|
||||||
|
v = tx_power - rssi - MAX_TRANSFORMED_RSSI
|
||||||
|
if v < 0:
|
||||||
|
v = 0
|
||||||
|
return v / (-MIN_RSSI)
|
||||||
|
|
||||||
|
|
||||||
|
def make_training_data(df: pd.DataFrame, device_to_map):
|
||||||
|
idx_to_tracker = dict(enumerate(df['tracker'].cat.categories ))
|
||||||
|
tracker_to_idx = {v: k for k, v in idx_to_tracker.items()}
|
||||||
|
idx_to_room = dict(enumerate(df['real_room'].cat.categories ))
|
||||||
|
room_to_idx = {v: k for k, v in idx_to_room.items()}
|
||||||
|
|
||||||
|
last_real_room = None
|
||||||
|
start_time = None
|
||||||
|
current_feature = [FAR_AWAY_FEATURE_VALUE] * len(idx_to_tracker)
|
||||||
|
|
||||||
|
features = []
|
||||||
|
labels = []
|
||||||
|
|
||||||
|
# Feature vectors - rssi column for each room
|
||||||
|
for i, row in df.iterrows():
|
||||||
|
time, device, tracker, rssi, tx_power, real_room = row
|
||||||
|
if device != device_to_map:
|
||||||
|
continue
|
||||||
|
if last_real_room != real_room:
|
||||||
|
start_time = time
|
||||||
|
last_real_room = real_room
|
||||||
|
|
||||||
|
tracker_idx = tracker_to_idx[tracker]
|
||||||
|
current_feature[tracker_idx] = get_feature_value(rssi, tx_power)
|
||||||
|
if time - start_time > 20:
|
||||||
|
features.append(copy(current_feature))
|
||||||
|
labels.append(room_to_idx[real_room])
|
||||||
|
|
||||||
|
return np.array(features), np.array(labels)
|
||||||
|
|
||||||
|
def train(features, labels, classes):
|
||||||
|
clf = svm.SVC(kernel='rbf')
|
||||||
|
print("Training")
|
||||||
|
scores = cross_val_score(clf, features, labels, cv=5)
|
||||||
|
print(scores)
|
||||||
|
print("%0.2f accuracy with a standard deviation of %0.2f" % (scores.mean(), scores.std()))
|
||||||
|
|
||||||
|
X_train, X_test, y_train, y_test = train_test_split(features, labels, random_state=0)
|
||||||
|
clf.fit(X_train, y_train)
|
||||||
|
cm = confusion_matrix(clf.predict(X_test), y_test)
|
||||||
|
print(cm)
|
||||||
|
print(classes)
|
||||||
|
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=classes)
|
||||||
|
disp.plot()
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
csv_path = Path("/home/martin/code/ansible/roles/bluetooth-monitor/other/collected.csv")
|
||||||
|
df = load_measurements(csv_path)
|
||||||
|
features, labels = make_training_data(df, "martins_apple_watch")
|
||||||
|
print(np.unique(labels))
|
||||||
|
print(features.shape, labels.shape)
|
||||||
|
train(features, labels, list(df['real_room'].dtype.categories))
|
||||||
383
roles/bluetooth-monitor/other/bt_monitor_analyze.ipynb
Normal file
383
roles/bluetooth-monitor/other/bt_monitor_analyze.ipynb
Normal file
File diff suppressed because one or more lines are too long
309
roles/bluetooth-monitor/other/bt_monitor_server.py
Executable file
309
roles/bluetooth-monitor/other/bt_monitor_server.py
Executable file
@@ -0,0 +1,309 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import aiomqtt
|
||||||
|
import json
|
||||||
|
import asyncio
|
||||||
|
from time import time
|
||||||
|
from pathlib import Path
|
||||||
|
from collections import namedtuple, defaultdict, deque
|
||||||
|
from typing import Dict, Optional, List
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import logging
|
||||||
|
from sklearn import svm
|
||||||
|
from sklearn.model_selection import cross_val_score
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
BtleMeasurement = namedtuple("BtleMeasurement", ["time", "tracker", "address", "rssi", "tx_power"])
|
||||||
|
BtleDeviceMeasurement = namedtuple("BtleDeviceMeasurement", ["time", "device", "tracker", "rssi", "tx_power"])
|
||||||
|
MqttInfo = namedtuple("MqttInfo", ["server", "username", "password"])
|
||||||
|
|
||||||
|
# ------------------------------------------------------- DECODING -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceDecoder:
|
||||||
|
"""Decode bluetooth addresses - either simple ones (just address to name) or random changing ones like Apple devices using irk keys"""
|
||||||
|
|
||||||
|
def __init__(self, irk_to_devicename: Dict[str, str], address_to_name: Dict[str, str]):
|
||||||
|
"""
|
||||||
|
address_to_name: dictionary from bt address as string separated by ":" to a device name
|
||||||
|
irk_to_devicename is dict with irk as a hex string, mapping to device name
|
||||||
|
"""
|
||||||
|
self.irk_to_devicename = {bytes.fromhex(k): v for k, v in irk_to_devicename.items()}
|
||||||
|
self.address_to_name = address_to_name
|
||||||
|
|
||||||
|
def _resolve_rpa(rpa: bytes, irk: bytes) -> bool:
|
||||||
|
"""Compares the random address rpa to an irk (secret key) and return True if it matches"""
|
||||||
|
assert len(rpa) == 6
|
||||||
|
assert len(irk) == 16
|
||||||
|
|
||||||
|
key = irk
|
||||||
|
plain_text = b"\x00" * 16
|
||||||
|
plain_text = bytearray(plain_text)
|
||||||
|
plain_text[15] = rpa[3]
|
||||||
|
plain_text[14] = rpa[4]
|
||||||
|
plain_text[13] = rpa[5]
|
||||||
|
plain_text = bytes(plain_text)
|
||||||
|
|
||||||
|
cipher = AES.new(key, AES.MODE_ECB)
|
||||||
|
cipher_text = cipher.encrypt(plain_text)
|
||||||
|
return cipher_text[15] == rpa[0] and cipher_text[14] == rpa[1] and cipher_text[13] == rpa[2]
|
||||||
|
|
||||||
|
def _addr_to_bytes(addr: str) -> bytes:
|
||||||
|
"""Converts a bluetooth mac address string with semicolons to bytes"""
|
||||||
|
str_without_colons = addr.replace(":", "")
|
||||||
|
bytearr = bytearray.fromhex(str_without_colons)
|
||||||
|
bytearr.reverse()
|
||||||
|
return bytes(bytearr)
|
||||||
|
|
||||||
|
def decode(self, addr: str) -> Optional[str]:
|
||||||
|
"""addr is a bluetooth address as a string e.g. 4d:24:12:12:34:10"""
|
||||||
|
for irk, name in self.irk_to_devicename.items():
|
||||||
|
if DeviceDecoder._resolve_rpa(DeviceDecoder._addr_to_bytes(addr), irk):
|
||||||
|
return name
|
||||||
|
return self.address_to_name.get(addr, None)
|
||||||
|
|
||||||
|
def __call__(self, m: BtleMeasurement) -> Optional[BtleDeviceMeasurement]:
|
||||||
|
decoded_device_name = self.decode(m.address)
|
||||||
|
if not decoded_device_name:
|
||||||
|
return None
|
||||||
|
return BtleDeviceMeasurement(m.time, decoded_device_name, m.tracker, m.rssi, m.tx_power)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------- MACHINE LEARNING ----------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class KnownRoomCsvLogger:
|
||||||
|
"""Logs known room measurements to be used later as training data for classifier"""
|
||||||
|
|
||||||
|
def __init__(self, csv_file: Path):
|
||||||
|
self.known_room = None
|
||||||
|
|
||||||
|
if csv_file.exists():
|
||||||
|
self.csv_file_handle = open(csv_file, "a")
|
||||||
|
else:
|
||||||
|
self.csv_file_handle = open(csv_file, "w")
|
||||||
|
print(f"#time,device,tracker,rssi,tx_power,known_room", file=csv_file)
|
||||||
|
|
||||||
|
def update_known_room(self, known_room: str):
|
||||||
|
if known_room != self.known_room:
|
||||||
|
logging.info(f"Updating known_room {self.known_room} -> {known_room}")
|
||||||
|
self.known_room = known_room
|
||||||
|
|
||||||
|
def report_measure(self, m: BtleDeviceMeasurement):
|
||||||
|
ignore_rooms = ("keins", "?", "none", "unknown")
|
||||||
|
if self.known_room is None or self.known_room in ignore_rooms:
|
||||||
|
return
|
||||||
|
logging.info(f"Appending to training set: {m}")
|
||||||
|
print(
|
||||||
|
f"{m.time},{m.device},{m.tracker},{m.rssi},{m.tx_power},{self.known_room}",
|
||||||
|
file=self.csv_file_handle,)
|
||||||
|
|
||||||
|
|
||||||
|
class RunningFeatureVector:
|
||||||
|
FAR_AWAY_FEATURE_VALUE = 1
|
||||||
|
MIN_TIME_UNTIL_PREDICTION = 40 # wait until every reachable tracker detected the device
|
||||||
|
TIME_TO_DELETE_IF_NOT_SEEN = 30 # if device wasn't spotted for this time period, the measure is set to inf
|
||||||
|
|
||||||
|
def __init__(self, trackers: List[str]):
|
||||||
|
self.trackers = trackers
|
||||||
|
self.feature_vecs_per_device = defaultdict(lambda: [self.FAR_AWAY_FEATURE_VALUE] * len(trackers))
|
||||||
|
self.last_measurements = deque()
|
||||||
|
self.tracker_name_to_idx = {name: i for i, name in enumerate(trackers)}
|
||||||
|
self.start_time = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_feature_value(rssi, tx_power):
|
||||||
|
"""Transforms rssi and tx power into a value between 0 and 1, where 0 is close and 1 is far away"""
|
||||||
|
MIN_RSSI = -90
|
||||||
|
MAX_TRANSFORMED_RSSI = 40
|
||||||
|
v = tx_power - rssi - MAX_TRANSFORMED_RSSI
|
||||||
|
if v < 0:
|
||||||
|
v = 0
|
||||||
|
return v / (-MIN_RSSI)
|
||||||
|
|
||||||
|
def add_measurement(self, new_measurement: BtleDeviceMeasurement):
|
||||||
|
if self.start_time is None:
|
||||||
|
self.start_time = new_measurement.time
|
||||||
|
|
||||||
|
self.last_measurements.append(new_measurement)
|
||||||
|
while len(self.last_measurements) > 0 and new_measurement.time - self.last_measurements[0].time > self.TIME_TO_DELETE_IF_NOT_SEEN:
|
||||||
|
self.last_measurements.popleft()
|
||||||
|
|
||||||
|
feature_vec = [self.FAR_AWAY_FEATURE_VALUE] * len(self.trackers)
|
||||||
|
for m in self.last_measurements:
|
||||||
|
if m.device == new_measurement.device:
|
||||||
|
tracker_idx = self.tracker_name_to_idx[m.tracker]
|
||||||
|
feature_vec[tracker_idx] = self._get_feature_value(m.rssi, m.tx_power)
|
||||||
|
return feature_vec if new_measurement.time - self.start_time > self.MIN_TIME_UNTIL_PREDICTION else None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def training_data_from_df(df: pd.DataFrame, device_to_train: str):
|
||||||
|
"""Returns a feature matrix (num_measurement, num_trackers) and a label vector (both numeric) to be used in scikit learn"""
|
||||||
|
trackers = list(df["tracker"].cat.categories)
|
||||||
|
idx_to_room = dict(enumerate(df["known_room"].cat.categories))
|
||||||
|
room_to_idx = {v: k for k, v in idx_to_room.items()}
|
||||||
|
|
||||||
|
last_known_room = None
|
||||||
|
|
||||||
|
features = []
|
||||||
|
labels = []
|
||||||
|
|
||||||
|
feature_accumulator = RunningFeatureVector(trackers)
|
||||||
|
|
||||||
|
# Feature vectors - rssi column for each room
|
||||||
|
for i, row in df.iterrows():
|
||||||
|
time, device, tracker, rssi, tx_power, known_room = row
|
||||||
|
m = BtleDeviceMeasurement(time, device, tracker, rssi, tx_power)
|
||||||
|
if device != device_to_train:
|
||||||
|
continue
|
||||||
|
if last_known_room != known_room:
|
||||||
|
feature_accumulator = RunningFeatureVector(trackers) # reset for new room
|
||||||
|
last_known_room = known_room
|
||||||
|
feature_vec = feature_accumulator.add_measurement(m)
|
||||||
|
if feature_vec is not None:
|
||||||
|
features.append(feature_vec)
|
||||||
|
labels.append(room_to_idx[known_room])
|
||||||
|
|
||||||
|
return np.array(features), np.array(labels)
|
||||||
|
|
||||||
|
|
||||||
|
def load_measurements_from_csv(csv_file: Path) -> pd.DataFrame:
|
||||||
|
"""Load csv with training data into dataframe"""
|
||||||
|
|
||||||
|
def cleanup_column_name(col_name: str):
|
||||||
|
return col_name.replace("#", "").strip()
|
||||||
|
|
||||||
|
df = pd.read_csv(str(csv_file))
|
||||||
|
|
||||||
|
# String cleanup in column names and room names
|
||||||
|
df = df.rename(columns=cleanup_column_name)
|
||||||
|
df.map(lambda x: x.strip() if isinstance(x, str) else x)
|
||||||
|
|
||||||
|
df["tracker"] = df["tracker"].astype("category")
|
||||||
|
df["known_room"] = df["known_room"].astype("category")
|
||||||
|
df['device'] = df['device'].astype("category")
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
async def send_discovery_messages(mqtt_client, device_names):
|
||||||
|
for device_name in device_names:
|
||||||
|
topic = f"homeassistant/sensor/my_btmonitor/{device_name}/config"
|
||||||
|
msg = {
|
||||||
|
"name": device_name,
|
||||||
|
"state_topic": f"my_btmonitor/ml/{device_name}",
|
||||||
|
"expire_after": 30,
|
||||||
|
"unique_id": device_name,
|
||||||
|
}
|
||||||
|
await mqtt_client.publish(topic, json.dumps(msg).encode(), retain=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def async_main(
|
||||||
|
mqtt_info: MqttInfo,
|
||||||
|
trackers: List[str],
|
||||||
|
devices: List[str],
|
||||||
|
classifier,
|
||||||
|
device_decoder: DeviceDecoder,
|
||||||
|
training_data_logger: KnownRoomCsvLogger,
|
||||||
|
):
|
||||||
|
current_rooms = defaultdict(lambda: "unknown")
|
||||||
|
feature_accumulator = RunningFeatureVector(trackers)
|
||||||
|
async with aiomqtt.Client(
|
||||||
|
hostname=mqtt_info.server, username=mqtt_info.username, password=mqtt_info.password
|
||||||
|
) as client:
|
||||||
|
await send_discovery_messages(client, devices)
|
||||||
|
await client.subscribe("my_btmonitor/#")
|
||||||
|
async for message in client.messages:
|
||||||
|
current_time = time()
|
||||||
|
topic = message.topic
|
||||||
|
if topic.value == "my_btmonitor/known_room":
|
||||||
|
training_data_logger.update_known_room(message.payload.decode())
|
||||||
|
else:
|
||||||
|
splitted_topic = message.topic.value.split("/")
|
||||||
|
if splitted_topic[0] == "my_btmonitor" and splitted_topic[1] == "raw_measurements":
|
||||||
|
msg_json = json.loads(message.payload)
|
||||||
|
measurement = BtleMeasurement(
|
||||||
|
time=current_time,
|
||||||
|
tracker=splitted_topic[2],
|
||||||
|
address=msg_json["address"],
|
||||||
|
rssi=msg_json["rssi"],
|
||||||
|
tx_power=msg_json.get("tx_power", 0),
|
||||||
|
)
|
||||||
|
logging.debug(f"Got Measurement {measurement}")
|
||||||
|
m = device_decoder(measurement)
|
||||||
|
if m is not None:
|
||||||
|
logging.info(f"Decoded Measurement {m}")
|
||||||
|
training_data_logger.report_measure(m)
|
||||||
|
feature_vec =feature_accumulator.add_measurement(m)
|
||||||
|
if feature_vec:
|
||||||
|
feature_str={tracker : value for tracker, value in zip(trackers, feature_vec)}
|
||||||
|
logging.info(f"Features: {feature_str}")
|
||||||
|
if feature_vec is not None and classifier is not None:
|
||||||
|
room = classifier(m.device, feature_vec)
|
||||||
|
if room != current_rooms[m.device]:
|
||||||
|
logging.info(f"{m.device} moved room {current_rooms[m.device]} to {room}")
|
||||||
|
current_rooms[m.device] = room
|
||||||
|
await client.publish(f"my_btmonitor/ml/{m.device}", room.encode())
|
||||||
|
|
||||||
|
async def async_main_with_restart(
|
||||||
|
mqtt_info: MqttInfo,
|
||||||
|
trackers: List[str],
|
||||||
|
devices: List[str],
|
||||||
|
classifier,
|
||||||
|
device_decoder: DeviceDecoder,
|
||||||
|
training_data_logger: KnownRoomCsvLogger,
|
||||||
|
):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
await async_main(mqtt_info, trackers, devices, classifier, device_decoder, training_data_logger)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print("restarting...")
|
||||||
|
|
||||||
|
def get_classification_func(training_df: pd.DataFrame, log_classifier_scores=True):
|
||||||
|
devices_to_track = list(training_df["device"].unique())
|
||||||
|
classifiers = {}
|
||||||
|
rooms = list(training_df["known_room"].dtype.categories)
|
||||||
|
for device_to_track in devices_to_track:
|
||||||
|
features, labels = training_data_from_df(training_df, device_to_track)
|
||||||
|
clf = svm.SVC(kernel="rbf")
|
||||||
|
logging.info(f"Computing cross validation score for {device_to_track}")
|
||||||
|
if log_classifier_scores:
|
||||||
|
scores = cross_val_score(clf, features, labels, cv=5)
|
||||||
|
logging.info(" %0.2f accuracy with a standard deviation of %0.2f" % (scores.mean(), scores.std()))
|
||||||
|
|
||||||
|
logging.info(f"Training SVM classifier for {device_to_track}")
|
||||||
|
clf.fit(features, labels)
|
||||||
|
classifiers[device_to_track] = clf
|
||||||
|
|
||||||
|
def classify(device_name, feature_vec):
|
||||||
|
room_idx = classifiers[device_name].predict([feature_vec])[0]
|
||||||
|
return rooms[room_idx]
|
||||||
|
|
||||||
|
return classify
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
mqtt_info = MqttInfo(server="homeassistant.fritz.box", username="my_btmonitor", password="8aBIAC14jaKKbla")
|
||||||
|
# Dict with bt addresses as strings to device name
|
||||||
|
address_to_name = {}
|
||||||
|
# Devices with random addresses - need irk key
|
||||||
|
irk_to_devicename = {
|
||||||
|
"aa67542b82c0e05d65c27fb7e313aba5": "martins_apple_watch",
|
||||||
|
"840e3892644c1ebd1594a9069c14ce0d": "martins_iphone",
|
||||||
|
}
|
||||||
|
script_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
data_file = Path(script_path) / Path("training_data.csv")
|
||||||
|
training_df = load_measurements_from_csv(data_file)
|
||||||
|
classification_func = get_classification_func(training_df)
|
||||||
|
training_data_logger = KnownRoomCsvLogger(data_file)
|
||||||
|
device_decoder = DeviceDecoder(irk_to_devicename, address_to_name)
|
||||||
|
trackers = list(training_df["tracker"].cat.categories)
|
||||||
|
devices = list(training_df['device'].cat.categories)
|
||||||
|
asyncio.run(async_main_with_restart(mqtt_info, trackers, devices, classification_func, device_decoder, training_data_logger))
|
||||||
27855
roles/bluetooth-monitor/other/collected_backup.csv
Normal file
27855
roles/bluetooth-monitor/other/collected_backup.csv
Normal file
File diff suppressed because it is too large
Load Diff
7
roles/bluetooth-monitor/other/requirements.txt
Normal file
7
roles/bluetooth-monitor/other/requirements.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
aiomqtt==2.0.0
|
||||||
|
numpy==1.26.4
|
||||||
|
pandas==2.2.1
|
||||||
|
pycryptodome==3.20.0
|
||||||
|
scikit-learn==1.4.1.post1
|
||||||
|
scipy==1.12.0
|
||||||
|
typing_extensions==4.10.0
|
||||||
27855
roles/bluetooth-monitor/other/training_data.csv
Normal file
27855
roles/bluetooth-monitor/other/training_data.csv
Normal file
File diff suppressed because it is too large
Load Diff
22
roles/bluetooth-monitor/tasks/main.yml
Normal file
22
roles/bluetooth-monitor/tasks/main.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
- name: Apt install bluez, firmware and Python requirements
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- bluez
|
||||||
|
- bluez-firmware
|
||||||
|
- firmware-realtek
|
||||||
|
- firmware-realtek-rtl8723cs-bt
|
||||||
|
- python3-pycryptodome
|
||||||
|
- python3-bleak
|
||||||
|
- python3-asyncio-mqtt
|
||||||
|
- python3-numpy
|
||||||
|
- name: Copy monitor script
|
||||||
|
template: src=my_btmonitor.py dest=/usr/bin/my_btmonitor owner=root mode=u+rwx
|
||||||
|
- name: Install systemd service file
|
||||||
|
copy: src=my_btmonitor.service dest=/etc/systemd/system/
|
||||||
|
- name: Add script to autostart and start now
|
||||||
|
systemd: name=my_btmonitor state=restarted enabled=yes daemon_reload=yes
|
||||||
|
#- name: Add to sysdweb
|
||||||
|
# include_role:
|
||||||
|
# name: pi-sysdweb
|
||||||
|
# vars:
|
||||||
|
# sysdweb_name: my_btmonitor
|
||||||
97
roles/bluetooth-monitor/templates/my_btmonitor.py
Normal file
97
roles/bluetooth-monitor/templates/my_btmonitor.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import asyncio
|
||||||
|
from bleak import BleakScanner
|
||||||
|
from bleak.assigned_numbers import AdvertisementDataType
|
||||||
|
from bleak.backends.bluezdbus.advertisement_monitor import OrPattern
|
||||||
|
from bleak.backends.bluezdbus.scanner import BlueZScannerArgs
|
||||||
|
from functools import partial
|
||||||
|
import asyncio_mqtt
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
# ------------------- Config ----------------------------------------------------------------
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"mqtt": {
|
||||||
|
"hostname": "homeassistant.fritz.box",
|
||||||
|
"username": "{{my_btmonitor_mqtt_username}}",
|
||||||
|
"password": "{{my_btmonitor_mqtt_password}}",
|
||||||
|
"room": "{{sensor_room_name_ascii}}"
|
||||||
|
},
|
||||||
|
"watchdog_seconds": {{my_bt_monitor_watchdog_seconds | default(None)}},
|
||||||
|
"restart_ble_interface": {{my_btmonitor_restart_ble_interface | default(None)}},
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_event = asyncio.Event()
|
||||||
|
time_last_package_received = datetime.now()
|
||||||
|
|
||||||
|
|
||||||
|
async def on_device_found_callback(mqtt_client, room, device, advertising_data):
|
||||||
|
global time_last_package_received
|
||||||
|
time_last_package_received = datetime.now()
|
||||||
|
rssi = advertising_data.rssi
|
||||||
|
tx_power = advertising_data.tx_power
|
||||||
|
if tx_power is not None and rssi is not None:
|
||||||
|
topic = f"my_btmonitor/raw_measurements/{room}"
|
||||||
|
data = {"address": device.address,
|
||||||
|
"rssi": rssi,
|
||||||
|
"tx_power": tx_power}
|
||||||
|
try:
|
||||||
|
await mqtt_client.publish(topic, json.dumps(data).encode())
|
||||||
|
except Exception:
|
||||||
|
print("Probably mqtt isn't running - exit whole script and let systemd restart it")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
async def watchdog():
|
||||||
|
global time_last_package_received
|
||||||
|
timeout = config["watchdog_seconds"]
|
||||||
|
if not timeout or timeout <= 0:
|
||||||
|
return
|
||||||
|
while True:
|
||||||
|
restart = (datetime.now() - time_last_package_received).seconds > timeout
|
||||||
|
if restart:
|
||||||
|
stop_event.set()
|
||||||
|
await asyncio.sleep(60)
|
||||||
|
|
||||||
|
|
||||||
|
async def ble_scan():
|
||||||
|
mqtt_conf = config['mqtt']
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
async with asyncio_mqtt.Client(hostname=mqtt_conf["hostname"],
|
||||||
|
username=mqtt_conf["username"],
|
||||||
|
password=mqtt_conf['password']) as mqtt_client:
|
||||||
|
cb = partial(on_device_found_callback, mqtt_client, mqtt_conf['room'])
|
||||||
|
active_scan = True
|
||||||
|
if active_scan:
|
||||||
|
async with BleakScanner(cb) as scanner:
|
||||||
|
await stop_event.wait()
|
||||||
|
else:
|
||||||
|
# Doesn't work, because of the strange or_patters
|
||||||
|
args = BlueZScannerArgs(
|
||||||
|
or_patterns=[OrPattern(0, AdvertisementDataType.MANUFACTURER_SPECIFIC_DATA, b"\x00\x4c")]
|
||||||
|
)
|
||||||
|
async with BleakScanner(cb, bluez=args, scanning_mode="passive") as scanner:
|
||||||
|
await stop_event.wait()
|
||||||
|
except Exception as e:
|
||||||
|
print("Error", e)
|
||||||
|
print("Starting again")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
await asyncio.gather(ble_scan(), watchdog())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
restart_interface = config["restart_ble_interface"]
|
||||||
|
if restart_interface:
|
||||||
|
print(f"Restarting {restart_interface}")
|
||||||
|
os.system(f"hciconfig {restart_interface} down")
|
||||||
|
time.sleep(3)
|
||||||
|
os.system(f"hciconfig {restart_interface} up")
|
||||||
|
time.sleep(3)
|
||||||
|
print("Done")
|
||||||
|
asyncio.run(main())
|
||||||
3
roles/pi-alsasetup/tasks/main.yml
Normal file
3
roles/pi-alsasetup/tasks/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
- name: Sync alsa config
|
||||||
|
template: src=asound.conf dest=/etc/asound.conf
|
||||||
3
roles/pi-dhtsensor-circuitpython/defaults/main.yml
Normal file
3
roles/pi-dhtsensor-circuitpython/defaults/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dht_pin: "D12"
|
||||||
|
dht_polling_sleep_time_seconds: 20
|
||||||
@@ -4,20 +4,21 @@
|
|||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import board
|
import multiprocessing
|
||||||
import adafruit_dht
|
|
||||||
|
|
||||||
config = json.load(open("/etc/dht22_sensing.json"))
|
config = json.load(open("/etc/dht22_sensing.json"))
|
||||||
dht_device = adafruit_dht.DHT22(getattr(board, config['dht_pin']))
|
|
||||||
|
|
||||||
|
|
||||||
def send_to_home_assistant(temperature, humidity):
|
def send_to_home_assistant(temperature, humidity):
|
||||||
|
# print(f"Sending temperature {temperature}, humidity {humidity}")
|
||||||
headers = {
|
headers = {
|
||||||
'x-ha-access': config['token'],
|
'x-ha-access': config['token'],
|
||||||
'Authorization': "Bearer {}".format(config['token'])
|
'Authorization': "Bearer {}".format(config['token'])
|
||||||
}
|
}
|
||||||
|
|
||||||
temperature_url = "{}/api/states/sensor.{}".format(config['ha_url'], config['ha_temp_sensor_name'])
|
temperature_url = "{}/api/states/sensor.{}".format(
|
||||||
|
config['ha_url'], config['ha_temp_sensor_name'])
|
||||||
temperatur_data = {
|
temperatur_data = {
|
||||||
"state": str(temperature),
|
"state": str(temperature),
|
||||||
"attributes": {
|
"attributes": {
|
||||||
@@ -28,7 +29,8 @@ def send_to_home_assistant(temperature, humidity):
|
|||||||
}
|
}
|
||||||
requests.post(temperature_url, json=temperatur_data, headers=headers)
|
requests.post(temperature_url, json=temperatur_data, headers=headers)
|
||||||
|
|
||||||
humidity_url = "{}/api/states/sensor.{}".format(config['ha_url'], config['ha_humidity_sensor_name'])
|
humidity_url = "{}/api/states/sensor.{}".format(
|
||||||
|
config['ha_url'], config['ha_humidity_sensor_name'])
|
||||||
humidity_data = {
|
humidity_data = {
|
||||||
"state": str(humidity),
|
"state": str(humidity),
|
||||||
"attributes": {
|
"attributes": {
|
||||||
@@ -41,13 +43,22 @@ def send_to_home_assistant(temperature, humidity):
|
|||||||
|
|
||||||
|
|
||||||
def sense():
|
def sense():
|
||||||
|
import adafruit_dht
|
||||||
|
import board
|
||||||
|
|
||||||
|
dht_device = adafruit_dht.DHT22(getattr(board, config['dht_pin']))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
send_to_home_assistant(dht_device.temperature, dht_device.humidity)
|
send_to_home_assistant(dht_device.temperature, dht_device.humidity)
|
||||||
except RuntimeError as error:
|
except RuntimeError as error:
|
||||||
# Errors happen fairly often, DHT's are hard to read, just keep going
|
# Errors happen fairly often, DHT's are hard to read, just keep going
|
||||||
print(error.args[0])
|
print(error.args[0])
|
||||||
time.sleep(config['polling_sleep_time_seconds'])
|
|
||||||
|
|
||||||
|
|
||||||
while True:
|
if __name__ == "__main__":
|
||||||
sense()
|
multiprocessing.set_start_method('spawn')
|
||||||
|
while True:
|
||||||
|
process = multiprocessing.Process(target=sense)
|
||||||
|
process.start()
|
||||||
|
process.join()
|
||||||
|
time.sleep(float(config['polling_sleep_time_seconds']))
|
||||||
20
roles/pi-dhtsensor-circuitpython/tasks/main.yml
Normal file
20
roles/pi-dhtsensor-circuitpython/tasks/main.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
- name: apt install libgpiod2
|
||||||
|
apt: name=libgpiod2 cache_valid_time=7200 state=present
|
||||||
|
- name: pip install adafruit-circuitpython-dht
|
||||||
|
pip:
|
||||||
|
name: adafruit-circuitpython-dht
|
||||||
|
executable: pip3
|
||||||
|
- name: Install script config
|
||||||
|
template: src=dht22_sensing.json dest=/etc/dht22_sensing.json
|
||||||
|
- name: Install script
|
||||||
|
copy: src=dht22_sensing.py dest=/usr/bin/dht22_sensing owner=root mode=u+rwx
|
||||||
|
- name: Install systemd service file
|
||||||
|
copy: src=dht22_sensing.service dest=/lib/systemd/system/
|
||||||
|
- name: Add script to autostart and start now
|
||||||
|
systemd: name=dht22_sensing state=started enabled=yes daemon_reload=yes
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: dht22_sensing
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"ha_url": "https://ha.bauer.tech",
|
"ha_url": "{{home_assistant_url}}",
|
||||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIxNjkxMWIzZmQ4ZWU0NDI0OTg0MjA0ZDllMDhkNGRlMCIsImlhdCI6MTU3ODE3MDU5MSwiZXhwIjoxODkzNTMwNTkxfQ.i7CdXEZy9DV9KPHAl-msK0rOfIUlPYo4zwwJ4UGhXuc",
|
"token": "{{home_assistant_token}}",
|
||||||
"dht_pin": "D12",
|
"dht_pin": "{{dht_pin}}",
|
||||||
"polling_sleep_time_seconds": 5,
|
"polling_sleep_time_seconds": "{{dht_polling_sleep_time_seconds}}",
|
||||||
|
|
||||||
"ha_temp_sensor_name": "{{sensor_room_name_ascii|lower}}_dht22_temperatur",
|
"ha_temp_sensor_name": "{{sensor_room_name_ascii|lower}}_dht22_temperatur",
|
||||||
"ha_temp_friendly_name": "{{sensor_room_name}} Temperatur",
|
"ha_temp_friendly_name": "{{sensor_room_name}} Temperatur",
|
||||||
BIN
roles/pi-dhtsensor/Adafruit_Python_DHT-master.zip
Normal file
BIN
roles/pi-dhtsensor/Adafruit_Python_DHT-master.zip
Normal file
Binary file not shown.
3
roles/pi-dhtsensor/defaults/main.yml
Normal file
3
roles/pi-dhtsensor/defaults/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dht_pin: 12
|
||||||
|
dht_polling_sleep_time_seconds: 20
|
||||||
Binary file not shown.
93
roles/pi-dhtsensor/files/dht22_sensing.py
Normal file
93
roles/pi-dhtsensor/files/dht22_sensing.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import Adafruit_DHT
|
||||||
|
|
||||||
|
config = json.load(open("/etc/dht22_sensing.json"))
|
||||||
|
|
||||||
|
|
||||||
|
class Filter:
|
||||||
|
def __init__(self, filter_length, max_mean_deviation):
|
||||||
|
self._length = filter_length
|
||||||
|
self.values = []
|
||||||
|
self.max_mean_deviation = max_mean_deviation
|
||||||
|
|
||||||
|
def mean(self):
|
||||||
|
return sum(self.values) / len(self.values)
|
||||||
|
|
||||||
|
def is_valid_value(self, val):
|
||||||
|
if len(self.values) == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return abs(val - self.mean()) < self.max_mean_deviation
|
||||||
|
|
||||||
|
def add_if_valid(self, val):
|
||||||
|
if val is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.is_valid_value(val):
|
||||||
|
self.values.append(val)
|
||||||
|
if len(self.values) > self._length:
|
||||||
|
self.values.pop(0)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_home_assistant(temperature, humidity):
|
||||||
|
# print(f"Sending temperature {temperature}, humidity {humidity}")
|
||||||
|
headers = {
|
||||||
|
'x-ha-access': config['token'],
|
||||||
|
'Authorization': "Bearer {}".format(config['token'])
|
||||||
|
}
|
||||||
|
|
||||||
|
temperature_url = "{}/api/states/sensor.{}".format(
|
||||||
|
config['ha_url'], config['ha_temp_sensor_name'])
|
||||||
|
temperatur_data = {
|
||||||
|
"state": "{:.1f}".format(temperature),
|
||||||
|
"attributes": {
|
||||||
|
"device_class": "temperature",
|
||||||
|
"friendly_name": config['ha_temp_friendly_name'],
|
||||||
|
"unit_of_measurement": "°C"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requests.post(temperature_url, json=temperatur_data, headers=headers)
|
||||||
|
|
||||||
|
humidity_url = "{}/api/states/sensor.{}".format(
|
||||||
|
config['ha_url'], config['ha_humidity_sensor_name'])
|
||||||
|
humidity_data = {
|
||||||
|
"state": "{:.1f}".format(humidity),
|
||||||
|
"attributes": {
|
||||||
|
"device_class": "humidity",
|
||||||
|
"friendly_name": config['ha_humidity_friendly_name'],
|
||||||
|
"unit_of_measurement": "%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requests.post(humidity_url, json=humidity_data, headers=headers)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
temp_filter = Filter(4, 10)
|
||||||
|
humidity_filter = Filter(4, 10)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
sensor = Adafruit_DHT.DHT22
|
||||||
|
humidity, temperature = Adafruit_DHT.read_retry(
|
||||||
|
sensor, config['dht_pin'])
|
||||||
|
|
||||||
|
temp_valid = temp_filter.add_if_valid(temperature)
|
||||||
|
humidity_valid = humidity_filter.add_if_valid(humidity)
|
||||||
|
if not (temp_valid and humidity_valid):
|
||||||
|
print(
|
||||||
|
f"Discarding value {temperature}C / {humidity}%, because to far from means {temp_filter.mean()}C, {humidity_filter.mean()}%")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
send_to_home_assistant(temperature, humidity)
|
||||||
|
except Exception as e:
|
||||||
|
print("Failed sending to home assistant")
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
time.sleep(float(config['polling_sleep_time_seconds']))
|
||||||
10
roles/pi-dhtsensor/files/dht22_sensing.service
Normal file
10
roles/pi-dhtsensor/files/dht22_sensing.service
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=DHT22 Temperature Humidity Sensing
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/dht22_sensing
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
31
roles/pi-dhtsensor/tasks/main.yml
Normal file
31
roles/pi-dhtsensor/tasks/main.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
# Use the deprecated version here instead, not the new circuitpython adafruit-circuitpython-dht version
|
||||||
|
# the new version needs a lot of CPU time and doesn't work correctly on old raspi 1
|
||||||
|
# a copy of the deprecated repo is downloaded as zip (if it goes away)
|
||||||
|
|
||||||
|
- name: Uninstall libgpiod2 (circuitpython) if present
|
||||||
|
apt: name=libgpiod2 cache_valid_time=7200 state=absent
|
||||||
|
- name: pip uninstall adafruit-circuitpython-dht
|
||||||
|
pip:
|
||||||
|
name: adafruit-circuitpython-dht
|
||||||
|
executable: pip3
|
||||||
|
state: absent
|
||||||
|
extra_args: "--break-system-packages"
|
||||||
|
- name: pip install adafruit-dht
|
||||||
|
pip:
|
||||||
|
name: adafruit-dht
|
||||||
|
executable: pip3
|
||||||
|
extra_args: "--break-system-packages"
|
||||||
|
- name: Install script config
|
||||||
|
template: src=dht22_sensing.json dest=/etc/dht22_sensing.json
|
||||||
|
- name: Install script
|
||||||
|
copy: src=dht22_sensing.py dest=/usr/bin/dht22_sensing owner=root mode=u+rwx
|
||||||
|
- name: Install systemd service file
|
||||||
|
copy: src=dht22_sensing.service dest=/etc/systemd/system/
|
||||||
|
- name: Add script to autostart and start now
|
||||||
|
systemd: name=dht22_sensing state=restarted enabled=yes daemon_reload=yes
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: dht22_sensing
|
||||||
12
roles/pi-dhtsensor/templates/dht22_sensing.json
Normal file
12
roles/pi-dhtsensor/templates/dht22_sensing.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"ha_url": "{{home_assistant_url}}",
|
||||||
|
"token": "{{home_assistant_token}}",
|
||||||
|
"dht_pin": "{{dht_pin}}",
|
||||||
|
"polling_sleep_time_seconds": "{{dht_polling_sleep_time_seconds}}",
|
||||||
|
|
||||||
|
"ha_temp_sensor_name": "{{sensor_room_name_ascii|lower}}_dht22_temperatur",
|
||||||
|
"ha_temp_friendly_name": "{{sensor_room_name}} Temperatur",
|
||||||
|
|
||||||
|
"ha_humidity_sensor_name": "{{sensor_room_name_ascii|lower}}_dht22_luftfeuchtigkeit",
|
||||||
|
"ha_humidity_friendly_name": "{{sensor_room_name}} Luftfeuchtigkeit"
|
||||||
|
}
|
||||||
4
roles/pi-disable-onboard-bluetooth/handlers/main.yml
Normal file
4
roles/pi-disable-onboard-bluetooth/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
- name: reboot
|
||||||
|
reboot:
|
||||||
|
|
||||||
7
roles/pi-disable-onboard-bluetooth/tasks/main.yml
Normal file
7
roles/pi-disable-onboard-bluetooth/tasks/main.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: Deactivate onboard bluetooth
|
||||||
|
lineinfile:
|
||||||
|
path: /boot/firmware/config.txt
|
||||||
|
regexp: "^#?dtoverlay=disable-bt"
|
||||||
|
line: "dtoverlay=disable-bt"
|
||||||
|
notify: reboot
|
||||||
4
roles/pi-hifiberry-amp/handlers/main.yml
Normal file
4
roles/pi-hifiberry-amp/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
- name: reboot
|
||||||
|
reboot:
|
||||||
|
|
||||||
3
roles/pi-hifiberry-amp/meta/main.yml
Normal file
3
roles/pi-hifiberry-amp/meta/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: pi-alsasetup
|
||||||
13
roles/pi-hifiberry-amp/tasks/main.yml
Normal file
13
roles/pi-hifiberry-amp/tasks/main.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
- name: Deactivate normal audio
|
||||||
|
lineinfile:
|
||||||
|
path: /boot/config.txt
|
||||||
|
regexp: "^#?dtparam=audio=on"
|
||||||
|
line: "#dtparam=audio=on"
|
||||||
|
notify: reboot
|
||||||
|
- name: Activate Hifiberry
|
||||||
|
lineinfile:
|
||||||
|
path: /boot/firmware/config.txt
|
||||||
|
regexp: "^#?dtoverlay=hifiberry-amp"
|
||||||
|
line: "dtoverlay={{hifiberry_overlay}}"
|
||||||
|
notify: reboot
|
||||||
53
roles/pi-irserver/files/hauppauge.rem
Normal file
53
roles/pi-irserver/files/hauppauge.rem
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
[REMOTE]
|
||||||
|
[NAME]hauppauge
|
||||||
|
|
||||||
|
[TIMING]
|
||||||
|
[0][N]0[RC]2[RP]87[FREQ]36[RC5]
|
||||||
|
|
||||||
|
[COMMANDS]
|
||||||
|
[BTN_1][T]0[D]11011110000001
|
||||||
|
[BTN_2][T]0[D]11011110000010
|
||||||
|
[BTN_3][T]0[D]11011110000011
|
||||||
|
[BTN_4][T]0[D]11011110000100
|
||||||
|
[BTN_5][T]0[D]11011110000101
|
||||||
|
[BTN_6][T]0[D]11011110000110
|
||||||
|
[BTN_7][T]0[D]11011110000111
|
||||||
|
[BTN_8][T]0[D]11011110001000
|
||||||
|
[BTN_9][T]0[D]11011110001001
|
||||||
|
[BTN_0][T]0[D]11011110000000
|
||||||
|
[KEY_NUMERIC_STAR][T]0[D]11011110001010
|
||||||
|
[KEY_NUMERIC_POUND][T]0[D]11011110001110
|
||||||
|
[KEY_RED][T]0[D]11011110001011
|
||||||
|
[KEY_GREEN][T]0[D]11011110101110
|
||||||
|
[KEY_YELLOW][T]0[D]11011110111000
|
||||||
|
[KEY_BLUE][T]0[D]11011110101001
|
||||||
|
[KEY_PLAY][T]0[D]11011110110101
|
||||||
|
[KEY_PAUSE][T]0[D]11011110110000
|
||||||
|
[KEY_RECORD][T]0[D]11011110110111
|
||||||
|
[KEY_PREVIOUS][T]0[D]11011110110010
|
||||||
|
[KEY_FORWARD][T]0[D]11011110110100
|
||||||
|
[KEY_REWIND][T]0[D]11011110100100
|
||||||
|
[KEY_FASTFORWARD][T]0[D]11011110011110
|
||||||
|
[KEY_VOLUMEUP][T]0[D]11011110010000
|
||||||
|
[KEY_VOLUMEDOWN][T]0[D]11011110010001
|
||||||
|
[KEY_CHANNELUP][T]0[D]11011110100000
|
||||||
|
[KEY_CHANNELDOWN][T]0[D]11011110100001
|
||||||
|
[KEY_MUTE][T]0[D]11011110001111
|
||||||
|
[KEY_CHANNEL][T]0[D]11011110010010
|
||||||
|
[KEY_EXIT][T]0[D]11011110011111
|
||||||
|
[KEY_MENU][T]0[D]11011110001101
|
||||||
|
[KEY_LEFT][T]0[D]11011110010110
|
||||||
|
[KEY_RIGHT][T]0[D]11011110010111
|
||||||
|
[KEY_UP][T]0[D]11011110010100
|
||||||
|
[KEY_DOWN][T]0[D]11011110010101
|
||||||
|
[KEY_OK][T]0[D]11011110100101
|
||||||
|
[KEY_POWER][T]0[D]11011110111101
|
||||||
|
[KEY_GOTO][T]0[D]11011110111011
|
||||||
|
[KEY_TV][T]0[D]11011110011100
|
||||||
|
[KEY_VIDEOS][T]0[D]11011110011000
|
||||||
|
[KEY_MUSIC][T]0[D]11011110011001
|
||||||
|
[KEY_PICTURES][T]0[D]11011110011010
|
||||||
|
[KEY_GUIDE][T]0[D]11011110011011
|
||||||
|
[KEY_RADIO][T]0[D]11011110001100
|
||||||
|
[KEY_STOP][T]0[D]11011110110110
|
||||||
|
|
||||||
BIN
roles/pi-irserver/files/irserver
Executable file
BIN
roles/pi-irserver/files/irserver
Executable file
Binary file not shown.
BIN
roles/pi-irserver/files/irserver-src.tar.gz
Normal file
BIN
roles/pi-irserver/files/irserver-src.tar.gz
Normal file
Binary file not shown.
17
roles/pi-irserver/files/irserver.readme
Normal file
17
roles/pi-irserver/files/irserver.readme
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
downloaded from
|
||||||
|
http://www.irtrans.de/de/download/linux.php
|
||||||
|
|
||||||
|
build on raspberry pi using
|
||||||
|
|
||||||
|
make arm_noccfgcc
|
||||||
|
|
||||||
|
-> linker errors with libzip
|
||||||
|
-> installed libzip-dev and added lzip to linker parameters
|
||||||
|
|
||||||
|
|
||||||
|
mkdir tmp
|
||||||
|
cd tmp
|
||||||
|
tar xf ../irserver-src.tar.gz
|
||||||
|
mkdir arm
|
||||||
|
# in makefile add: LDFLAGS = -lzip
|
||||||
|
make irserver_arm_noccf
|
||||||
10
roles/pi-irserver/files/irserver.service
Normal file
10
roles/pi-irserver/files/irserver.service
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=IR server for IR remotes
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/irserver /dev/ttyUSB0
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
BIN
roles/pi-irserver/files/irserver_old
Executable file
BIN
roles/pi-irserver/files/irserver_old
Executable file
Binary file not shown.
16
roles/pi-irserver/tasks/main.yml
Normal file
16
roles/pi-irserver/tasks/main.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
- name: Copy irserver
|
||||||
|
copy: src=irserver dest=/usr/bin/irserver mode=u+rx
|
||||||
|
- name: Make config dir for remotes
|
||||||
|
file: path=/usr/bin/remotes state=directory
|
||||||
|
- name: Copy hauppauge remote
|
||||||
|
copy: src=hauppauge.rem dest=/usr/bin/remotes/
|
||||||
|
- name: Copy irserver systemd file
|
||||||
|
copy: src=irserver.service dest=/lib/systemd/system/
|
||||||
|
- name: Enable irserver autostart
|
||||||
|
systemd: name=irserver state=restarted enabled=yes daemon_reload=yes
|
||||||
|
- name: Add irserver to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: irserver
|
||||||
32
roles/pi-knxd/README
Normal file
32
roles/pi-knxd/README
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
Infos zum Stick
|
||||||
|
http://busware.de/tiki-index.php?page=TUL
|
||||||
|
|
||||||
|
Blog mit Tutorial:
|
||||||
|
https://www.meintechblog.de/2018/07/tul-stick-als-knx-ip-gateway-auf-dem-raspberry-pi-einrichten-mit-knxd/
|
||||||
|
|
||||||
|
|
||||||
|
Programmieren
|
||||||
|
--------------
|
||||||
|
apt-get -y install dfu-programmer
|
||||||
|
|
||||||
|
Beim Einstecken des Sticks programmiertaste auf unterseite gedrueckt halten
|
||||||
|
|
||||||
|
sudo dfu-programmer atmega32u4 erase && sudo dfu-programmer atmega32u4 flash TPUARTtransparent.hex && sudo dfu-programmer atmega32u4 reset && sudo reboot
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
udev-rules
|
||||||
|
----------
|
||||||
|
|
||||||
|
udevadm info -a -n /dev/ttyACM0 | grep '{idProduct}' | head -n1
|
||||||
|
udevadm info -a -n /dev/ttyACM0 | grep '{idVendor}' | head -n1
|
||||||
|
udevadm info -a -n /dev/ttyACM0 | grep '{serial}' | head -n1
|
||||||
|
|
||||||
|
# my KNX stick
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204b", ATTRS{serial}=="8543934393935171A0B1", SYMLINK+="ttyKNX"
|
||||||
|
|
||||||
|
# z-wave stick
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0658", ATTRS{idProduct}=="0200", SYMLINK+="ttyZWave"
|
||||||
|
|
||||||
|
# HomeMatic stick (for fhem)
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204b", ATTRS{product}=="CUL868", SYMLINK+="ttyHomeMatic"
|
||||||
255
roles/pi-knxd/TPUARTtransparent.hex
Normal file
255
roles/pi-knxd/TPUARTtransparent.hex
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
:1000000093C00000ACC00000AAC00000A8C000005F
|
||||||
|
:10001000A6C00000A4C00000A2C00000A0C0000054
|
||||||
|
:100020009EC000009CC0000008C40000CBC30000BC
|
||||||
|
:1000300096C0000094C0000092C0000090C0000074
|
||||||
|
:100040008EC000008CC000008AC0000088C0000084
|
||||||
|
:1000500086C0000084C0000082C0000080C0000094
|
||||||
|
:100060007EC0000081C000007AC0000078C000009F
|
||||||
|
:1000700076C0000074C0000072C0000070C00000B4
|
||||||
|
:100080006EC000006CC000006AC0000068C00000C4
|
||||||
|
:1000900066C0000064C0000062C0000060C00000D4
|
||||||
|
:1000A0005EC000005CC000005AC0000012011001D8
|
||||||
|
:1000B00002000008EB034B2001000102DC010902F1
|
||||||
|
:1000C0003E00020100C032090400000102020100EA
|
||||||
|
:1000D000052400100104240206052406000107057A
|
||||||
|
:1000E00082030800FF09040100020A00000007055E
|
||||||
|
:1000F0000402100001070583021000010403090433
|
||||||
|
:1001000018036200750073007700610072006500DB
|
||||||
|
:100110002E006400650000003003540050005500BC
|
||||||
|
:10012000410052005400000011241FBECFEFDAE05E
|
||||||
|
:10013000DEBFCDBF11E0A0E0B1E0E8EBFFE002C020
|
||||||
|
:1001400005900D92AC31B107D9F712E0ACE1B1E006
|
||||||
|
:1001500001C01D92A034B107E1F76FD02BC750CF7B
|
||||||
|
:1001600088980895889808951F920F920FB60F925D
|
||||||
|
:1001700011242F933F938F939F93CF93DF93EF930C
|
||||||
|
:10018000FF939091CE008EB38430D9F4C8EAD1E0C9
|
||||||
|
:10019000E881F98190838881998101969983888388
|
||||||
|
:1001A0002E813F818217930721F48C819D81998351
|
||||||
|
:1001B00088832FB7F8948A859B8501969B878A87C9
|
||||||
|
:1001C0002FBFFF91EF91DF91CF919F918F913F9141
|
||||||
|
:1001D0002F910F900FBE0F901F901895089580E0FB
|
||||||
|
:1001E00091E03CC680E091E012C684B7877F84BF6F
|
||||||
|
:1001F00088E10FB6F89480936000109260000FBE03
|
||||||
|
:1002000080E090E020E80FB6F8942093610080939E
|
||||||
|
:1002100061000FBE809A8898B8D287E690E09093EC
|
||||||
|
:10022000CD008093CC0086E28093CA0082E0809368
|
||||||
|
:10023000C80088E98093C9000895EF92FF920F9358
|
||||||
|
:100240001F93CF93DF93D1DFECE1F1E02FB7F89468
|
||||||
|
:1002500088E291E091838083938382839583848372
|
||||||
|
:1002600088EA91E09783868380E890E09187808791
|
||||||
|
:10027000138612862FBFE8EAF1E02FB7F89484EBDB
|
||||||
|
:1002800091E091838083938382839583848384E345
|
||||||
|
:1002900092E09783868380E890E091878087138639
|
||||||
|
:1002A00012862FBF889878940CE111E088EAE82E36
|
||||||
|
:1002B00081E0F82E8EB391B39E7F20E0843009F464
|
||||||
|
:1002C00021E0922B91BB8FB7F894209126013091B9
|
||||||
|
:1002D00027018FBF809124019091250128173907AC
|
||||||
|
:1002E00001F180E091E0E8D497FD1BC08898E80117
|
||||||
|
:1002F000E881F98180838881998101969983888337
|
||||||
|
:100300002E813F818217930721F48C819D819983EF
|
||||||
|
:1003100088832FB7F8948A859B8501969B878A8767
|
||||||
|
:100320002FBF8FB7F894C091B201D091B3018FBFA6
|
||||||
|
:10033000209721F18898E091AA01F091AB0180E02B
|
||||||
|
:1003400091E060813FD58823C9F4F70182819381D0
|
||||||
|
:10035000019693838283268137818217930721F444
|
||||||
|
:1003600084819581938382832FB7F89482859385C6
|
||||||
|
:100370000197938782872FBF2197E9F62FB7F894CB
|
||||||
|
:1003800080912601909127012FBF892BF1F0889849
|
||||||
|
:10039000E801EA81FB813191FB83EA838E819F81B1
|
||||||
|
:1003A000E817F90721F48C819D819B838A832FB7FD
|
||||||
|
:1003B000F8948A859B8501979B878A872FBF8091B8
|
||||||
|
:1003C000C80085FFFCCF3093CE0080E091E0E3D4FD
|
||||||
|
:1003D0005FD470CFDA01923049F0933061F0913000
|
||||||
|
:1003E000F9F4ECEAF0E022E130E01EC0EEEBF0E0E0
|
||||||
|
:1003F0002EE330E019C0813049F0813018F08230AE
|
||||||
|
:1004000079F408C0ECEFF0E0849107C0E0E0F1E09F
|
||||||
|
:10041000849103C0E8E1F1E08491282F30E004C02A
|
||||||
|
:10042000E0E0F0E020E030E0ED93FC93C9010895B6
|
||||||
|
:100430009C0140913E0250913F024617570718F425
|
||||||
|
:10044000F90190E044C06115710511F0AB01F8CFDE
|
||||||
|
:100450008091E8008E778093E80040E050E0F0CF94
|
||||||
|
:100460008EB3882309F444C0853009F443C08091D9
|
||||||
|
:10047000E80083FF02C081E008958091E80082FDDA
|
||||||
|
:1004800031C08091E80080FF22C08091F3009091FC
|
||||||
|
:10049000F200782F60E0292F30E0262B372B07C0A1
|
||||||
|
:1004A00081918093F100415050402F5F3F4F4115A3
|
||||||
|
:1004B000510519F02830310598F390E028303105C6
|
||||||
|
:1004C00009F491E08091E8008E778093E80041156F
|
||||||
|
:1004D000510531F6992321F605C08EB3882341F0EA
|
||||||
|
:1004E000853041F08091E80082FFF7CF80E00895E9
|
||||||
|
:1004F00082E0089583E008959C0140913E0250916E
|
||||||
|
:100500003F024617570718F4F90190E045C06115FE
|
||||||
|
:10051000710511F0AB01F8CF8091E8008E778093E0
|
||||||
|
:10052000E80040E050E0F0CF8EB3882309F446C0E5
|
||||||
|
:10053000853009F445C08091E80083FF02C081E066
|
||||||
|
:1005400008958091E80082FD33C08091E80080FF2B
|
||||||
|
:1005500023C08091F3009091F200782F60E0292F62
|
||||||
|
:1005600030E0262B372B08C084918093F100319620
|
||||||
|
:10057000415050402F5F3F4F4115510519F0283031
|
||||||
|
:10058000310590F390E02830310509F491E0809135
|
||||||
|
:10059000E8008E778093E8004115510529F69923EC
|
||||||
|
:1005A00009F0C2CF05C08EB3882341F0853041F0F9
|
||||||
|
:1005B0008091E80082FFF7CF80E0089582E00895FF
|
||||||
|
:1005C00083E008958F708093E9008091EB00816053
|
||||||
|
:1005D0008093EB001092ED006093EC004093ED00EF
|
||||||
|
:1005E0008091EE00881F8827881F08950F931F931E
|
||||||
|
:1005F000CF93DF93062FEC0110E02AC09881992356
|
||||||
|
:1006000029F16B81E981FA812C81892F8F708730E4
|
||||||
|
:1006100018F5223010F452E001C056E040E028E026
|
||||||
|
:1006200030E003C04F5F220F331F2E173F07D0F378
|
||||||
|
:100630006295660F660F607C991F9927991F692B39
|
||||||
|
:100640004295407F452BBEDF882331F01F5F259602
|
||||||
|
:100650001017A0F281E001C080E0DF91CF911F91DF
|
||||||
|
:100660000F9108958091380288238CF403C08EB3D3
|
||||||
|
:100670008823B1F08091E80082FFF9CF8091E800F3
|
||||||
|
:100680008B778093E80008958EB3882349F080919A
|
||||||
|
:10069000E80080FFF9CF8091E8008E778093E80032
|
||||||
|
:1006A00008954091E4005091E50024E68091EC002B
|
||||||
|
:1006B00080FF23C08091E80080FD1DC08EB3882399
|
||||||
|
:1006C00011F482E00895853011F483E0089580915B
|
||||||
|
:1006D000EB0085FF02C081E008958091E4009091D5
|
||||||
|
:1006E000E5008417950711F3222311F484E008959F
|
||||||
|
:1006F0002150AC01DBCF80E008958091E80082FFBB
|
||||||
|
:10070000DDCFF9CFEF92FF920F931F934ED055D0CC
|
||||||
|
:1007100008ED10E0F80180818F7780838081806808
|
||||||
|
:10072000808380818F7D808319BC1EBA1092340231
|
||||||
|
:10073000109236021092350280EEE82EF12CF7016D
|
||||||
|
:1007400080818B7F8083F80180818160808380E05D
|
||||||
|
:1007500060E042E037DFE1EEF0E080818E7F808371
|
||||||
|
:10076000E2EEF0E080818160808380818860808318
|
||||||
|
:10077000F70180818E7F8083F80180818061808392
|
||||||
|
:100780001F910F91FF90EF900895E8EDF0E08081C8
|
||||||
|
:100790008F7E8083E7EDF0E080818160808384E05C
|
||||||
|
:1007A00082BF81E080933702ADCFE8EDF0E0808139
|
||||||
|
:1007B0008E7F80831092E20008951092DA001092EA
|
||||||
|
:1007C000E10008951F920F920FB60F9211241F930C
|
||||||
|
:1007D0002F933F934F935F936F937F938F939F9349
|
||||||
|
:1007E000AF93BF93EF93FF931091E9008091EC00DA
|
||||||
|
:1007F0001092E9008091F000877F8093F000789458
|
||||||
|
:10080000E0D01092E9008091F00088608093F000C1
|
||||||
|
:100810001F701093E900FF91EF91BF91AF919F91ED
|
||||||
|
:100820008F917F916F915F914F913F912F911F9188
|
||||||
|
:100830000F900FBE0F901F9018951F920F920FB63A
|
||||||
|
:100840000F9211242F933F934F935F936F937F9356
|
||||||
|
:100850008F939F93AF93BF93EF93FF938091E100AA
|
||||||
|
:1008600082FF0AC08091E20082FF06C08091E10011
|
||||||
|
:100870008B7F8093E1000BD28091DA0080FF1BC058
|
||||||
|
:100880008091D80080FF17C08091DA008E7F80931E
|
||||||
|
:10089000DA008091D90080FF0BC080E189BD82E140
|
||||||
|
:1008A00089BD09B400FEFDCF81E08EBB59DC03C0D9
|
||||||
|
:1008B00019BC1EBA57DC8091E10080FF17C08091FF
|
||||||
|
:1008C000E20080FF13C08091E2008E7F8093E200FF
|
||||||
|
:1008D0008091E20080618093E2008091D800806284
|
||||||
|
:1008E0008093D80019BC85E08EBBD1D18091E10006
|
||||||
|
:1008F00084FF2CC08091E20084FF28C080E189BD84
|
||||||
|
:1009000082E189BD09B400FEFDCF8091D8008F7DC2
|
||||||
|
:100910008093D8008091E1008F7E8093E1008091E8
|
||||||
|
:10092000E2008F7E8093E2008091E20081608093FC
|
||||||
|
:10093000E20080913402882331F48091E30087FD46
|
||||||
|
:1009400002C081E001C084E08EBBA1D18091E100B2
|
||||||
|
:1009500083FF26C08091E20083FF22C08091E100E6
|
||||||
|
:10096000877F8093E10082E08EBB109234028091F9
|
||||||
|
:10097000E1008E7F8093E1008091E2008E7F809382
|
||||||
|
:10098000E2008091E20080618093E20080E060E01C
|
||||||
|
:1009900042E018DE8091F00088608093F00077D10B
|
||||||
|
:1009A000FF91EF91BF91AF919F918F917F916F9147
|
||||||
|
:1009B0005F914F913F912F910F900FBE0F901F901D
|
||||||
|
:1009C00018951F93DF93CF93CDB7DEB7AC970FB6D3
|
||||||
|
:1009D000F894DEBF0FBECDBFE8E3F2E08091F100F6
|
||||||
|
:1009E000819322E0E034F207C9F7F9DB8091E80057
|
||||||
|
:1009F00083FF35C18091380230913902353009F4D6
|
||||||
|
:100A000087C0363040F43130C9F1313070F03330C6
|
||||||
|
:100A100009F025C133C0383009F4F4C0393009F485
|
||||||
|
:100A200003C1363009F01BC195C0803821F08238EF
|
||||||
|
:100A300009F015C108C090913502809136028823D3
|
||||||
|
:100A400099F0926011C080913C028F708093E90010
|
||||||
|
:100A50008091EB0090E025E0969587952A95E1F747
|
||||||
|
:100A6000982F91701092E9008091E800877F809321
|
||||||
|
:100A7000E8009093F1001092F100CFC0882319F0A4
|
||||||
|
:100A8000823009F0ECC090E08F719070009721F0F7
|
||||||
|
:100A9000029709F0E4C00CC080913A02813009F05D
|
||||||
|
:100AA000DEC010923602333069F5809336022AC0D8
|
||||||
|
:100AB00080913A02882331F520913C022F7009F48D
|
||||||
|
:100AC000CEC02093E9008091EB0080FF1BC0333043
|
||||||
|
:100AD00021F48091EB00806213C08091EB00806173
|
||||||
|
:100AE0008093EB0081E090E002C0880F991F2A9567
|
||||||
|
:100AF000E2F78093EA001092EA008091EB008860B0
|
||||||
|
:100B00008093EB001092E9008091E800877F88C015
|
||||||
|
:100B1000882309F0A4C010913A021F778091E30066
|
||||||
|
:100B20008078812B8093E3008091E800877F809319
|
||||||
|
:100B3000E80098DD8091E80080FFFCCF8091E30021
|
||||||
|
:100B400080688093E300112311F482E001C083E008
|
||||||
|
:100B50008EBB85C08058823008F081C080913A02F7
|
||||||
|
:100B600090913B0223E08C3D920709F033C083E073
|
||||||
|
:100B70008C838AE28B837FB7F894DE0115966EE052
|
||||||
|
:100B800040E050E011E2E62FF0E01093570084912E
|
||||||
|
:100B900040FF03C082958F706F5F8F70282F30E009
|
||||||
|
:100BA0008A3018F0C901C79602C0C901C0968D935A
|
||||||
|
:100BB0009D934F5F5F4F4431510529F77FBF80916F
|
||||||
|
:100BC000E800877F8093E800CE0103966AE270E038
|
||||||
|
:100BD0002FDC12C060913C02AE014F5F5F4FFADB29
|
||||||
|
:100BE000BC01009709F43BC08091E800877F8093A7
|
||||||
|
:100BF000E80089819A8180DC8091E8008B7780937E
|
||||||
|
:100C0000E8002DC0803859F58091E800877F8093F7
|
||||||
|
:100C1000E800809134028093F1008091E8008E77A3
|
||||||
|
:100C20008093E8001FDD1BC08823C9F490913A022D
|
||||||
|
:100C30009230A8F48091E800877F8093E800909339
|
||||||
|
:100C4000340210DD80913402882331F48091E30076
|
||||||
|
:100C500087FD02C081E001C084E08EBBC3DA8091D1
|
||||||
|
:100C6000E80083FF0AC08091E800877F8093E80056
|
||||||
|
:100C70008091EB0080628093EB00AC960FB6F89405
|
||||||
|
:100C8000DEBF0FBECDBFCF91DF911F9108950895B4
|
||||||
|
:100C90001F938EB3882371F01091E9008091EC00CE
|
||||||
|
:100CA0001092E9008091E80083FF01C08ADE1F7086
|
||||||
|
:100CB0001093E9001F910895FC018EB3843099F5DB
|
||||||
|
:100CC00084899589A689B7890097A105B10559F14D
|
||||||
|
:100CD00086818F708093E9008091E80082FF23C0B5
|
||||||
|
:100CE0004091F3002091F200942F80E030E0822BBD
|
||||||
|
:100CF000932B892B19F46FEF7FEF04C08091F100E3
|
||||||
|
:100D0000682F70E04091F3002091F200942F80E072
|
||||||
|
:100D100030E0822B932B892B41F48091E8008B7774
|
||||||
|
:100D20008093E80002C06FEF7FEFCB010895089534
|
||||||
|
:100D3000FC018EB3843051F584899589A689B789E1
|
||||||
|
:100D40000097A105B10511F181818F708093E900B1
|
||||||
|
:100D50004091F3002091F200942F80E030E0822B4C
|
||||||
|
:100D6000932B892BA9F09091E8008091E8008E7771
|
||||||
|
:100D70008093E80095FD0CC094DC982F882349F4FB
|
||||||
|
:100D80008091E8008E778093E80003C092E001C074
|
||||||
|
:100D900090E0892F0895FC018EB3843091F484890A
|
||||||
|
:100DA0009589A689B7890097A105B10551F0818180
|
||||||
|
:100DB0008F708093E9008091E80080FF02C0CF012E
|
||||||
|
:100DC000B7CF08951F93FC01162F8EB38430E1F442
|
||||||
|
:100DD00084899589A689B7890097A105B105A1F0F5
|
||||||
|
:100DE00081818F708093E9008091E80085FD08C0C3
|
||||||
|
:100DF0008091E8008E778093E80053DC882329F403
|
||||||
|
:100E00001093F10080E001C082E01F910895CF931C
|
||||||
|
:100E1000DF93EC014096FC018BE0DF011D928A9587
|
||||||
|
:100E2000E9F782E08C83898783E08E87CE01019683
|
||||||
|
:100E300061E0DCDB882371F0CE01069661E0D6DB51
|
||||||
|
:100E4000882341F0CE010B9661E0D0DB882319F0B6
|
||||||
|
:100E500081E001C080E0DF91CF910895CF93DF93CF
|
||||||
|
:100E6000EC018091E80083FFA2C0888190E020918E
|
||||||
|
:100E70003C0230913D022817390709F098C0809153
|
||||||
|
:100E80003902813269F0823220F4803209F08FC059
|
||||||
|
:100E90003CC0823209F46AC0833209F088C079C04C
|
||||||
|
:100EA00080913802813A09F082C08091E800877F02
|
||||||
|
:100EB0008093E8008091E80080FFFCCF8C899D89B9
|
||||||
|
:100EC000AE89BF898093F100292F3A2F4B2F5527E8
|
||||||
|
:100ED0002093F1009D01442755272093F1008B2F8B
|
||||||
|
:100EE0009927AA27BB278093F100888D8093F10072
|
||||||
|
:100EF000898D8093F1008A8D8093F1008091E800C4
|
||||||
|
:100F00008E778093E800AEDB52C080913802813248
|
||||||
|
:100F100009F04DC08091E800877F8093E80004C00D
|
||||||
|
:100F20008EB3882309F443C08091E80082FFF8CF94
|
||||||
|
:100F30008091F1009091F1002091F1003091F10049
|
||||||
|
:100F40008C8B9D8B2E8B3F8B8091F100888F8091B5
|
||||||
|
:100F5000F100898F8091F1008A8F8091E8008B7772
|
||||||
|
:100F60008093E8007FDBCE0139D921C0809138021F
|
||||||
|
:100F70008132E9F48091E800877F8093E80072DB9A
|
||||||
|
:100F800080913A0290913B02998B888BCE01CFDE03
|
||||||
|
:100F90000EC080913802813251F48091E800877F41
|
||||||
|
:100FA0008093E8005FDBCE0160913A02C0DEDF9102
|
||||||
|
:080FB000CF910895F894FFCFE2
|
||||||
|
:100FB80000831000000104100000018208000001F5
|
||||||
|
:0C0FC8000000000000000000000000001D
|
||||||
|
:00000001FF
|
||||||
8
roles/pi-knxd/flash-firmware.sh
Normal file
8
roles/pi-knxd/flash-firmware.sh
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
apt-get -y install dfu-programmer
|
||||||
|
|
||||||
|
dfu-programmer atmega32u4 erase
|
||||||
|
dfu-programmer atmega32u4 flash TPUARTtransparent.hex
|
||||||
|
dfu-programmer atmega32u4 reset
|
||||||
|
|
||||||
2
roles/pi-lirc/files/lircd.conf
Normal file
2
roles/pi-lirc/files/lircd.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
include "hauppauge.conf"
|
||||||
|
include "small_led_remote.conf"
|
||||||
350
roles/pi-lirc/files/small_led_remote.conf
Normal file
350
roles/pi-lirc/files/small_led_remote.conf
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
|
||||||
|
begin remote
|
||||||
|
|
||||||
|
name small_led_remote
|
||||||
|
flags RAW_CODES|CONST_LENGTH
|
||||||
|
eps 30
|
||||||
|
aeps 100
|
||||||
|
|
||||||
|
gap 108076
|
||||||
|
|
||||||
|
begin raw_codes
|
||||||
|
|
||||||
|
name ON
|
||||||
|
9257 4452 619 534 611 510
|
||||||
|
637 509 611 535 611 510
|
||||||
|
663 485 609 535 635 486
|
||||||
|
635 1637 640 1610 613 1636
|
||||||
|
641 1637 637 485 663 1611
|
||||||
|
615 1637 613 1637 666 1612
|
||||||
|
639 1612 615 537 608 509
|
||||||
|
661 486 609 535 636 485
|
||||||
|
637 510 609 535 611 510
|
||||||
|
662 1628 599 1636 640 1612
|
||||||
|
642 1637 639 1613 615 1637
|
||||||
|
642
|
||||||
|
|
||||||
|
name OFF
|
||||||
|
9068 4464 602 539 597 511
|
||||||
|
625 511 598 537 599 511
|
||||||
|
625 512 597 537 599 513
|
||||||
|
624 1643 599 1640 604 1640
|
||||||
|
630 1641 603 511 626 1642
|
||||||
|
603 1641 627 1618 630 511
|
||||||
|
599 1641 632 512 599 538
|
||||||
|
598 512 626 511 599 539
|
||||||
|
598 512 625 1641 603 511
|
||||||
|
626 1642 604 1641 603 1641
|
||||||
|
655 1617 603 1641 604 1640
|
||||||
|
629
|
||||||
|
|
||||||
|
name RED
|
||||||
|
9237 4461 602 547 597 519
|
||||||
|
625 521 597 545 600 518
|
||||||
|
626 520 598 547 596 521
|
||||||
|
624 1646 603 1646 602 1645
|
||||||
|
631 1647 601 524 621 1645
|
||||||
|
605 1645 604 1647 629 522
|
||||||
|
595 548 596 1645 604 550
|
||||||
|
594 525 620 520 597 548
|
||||||
|
596 523 622 1649 601 1647
|
||||||
|
602 547 599 1643 606 1644
|
||||||
|
631 1645 604 1645 604 1645
|
||||||
|
631
|
||||||
|
|
||||||
|
name GREEN
|
||||||
|
9103 4437 624 513 622 487
|
||||||
|
648 487 622 512 622 487
|
||||||
|
648 487 622 513 621 487
|
||||||
|
652 1616 621 1619 623 1617
|
||||||
|
651 1617 624 486 649 1618
|
||||||
|
624 1616 624 1617 651 1617
|
||||||
|
625 486 649 1618 624 486
|
||||||
|
649 486 622 512 623 486
|
||||||
|
648 486 622 512 623 1617
|
||||||
|
625 511 623 1618 623 1618
|
||||||
|
651 1617 624 1618 625 1617
|
||||||
|
651
|
||||||
|
|
||||||
|
|
||||||
|
name BLUE
|
||||||
|
9024 4501 609 524 613 494
|
||||||
|
638 498 610 522 611 498
|
||||||
|
634 501 610 523 613 496
|
||||||
|
635 1632 563 1680 560 1680
|
||||||
|
640 1629 609 499 675 1594
|
||||||
|
646 1596 620 1622 647 488
|
||||||
|
619 1621 647 1621 620 489
|
||||||
|
645 490 618 517 613 495
|
||||||
|
640 494 562 1679 589 547
|
||||||
|
562 572 564 1677 563 1679
|
||||||
|
589 1678 610 1632 608 1634
|
||||||
|
641
|
||||||
|
|
||||||
|
name WHITE
|
||||||
|
9083 4471 590 547 586 523
|
||||||
|
614 522 587 548 588 520
|
||||||
|
614 521 590 545 588 520
|
||||||
|
616 1650 591 1651 590 1652
|
||||||
|
618 1650 591 521 615 1649
|
||||||
|
592 1651 592 1649 618 1650
|
||||||
|
591 1650 591 1651 616 521
|
||||||
|
588 547 586 523 614 524
|
||||||
|
584 550 586 520 615 520
|
||||||
|
587 547 587 1652 589 1655
|
||||||
|
616 1650 589 1653 591 1655
|
||||||
|
615
|
||||||
|
|
||||||
|
name 1_ORANGE
|
||||||
|
9076 4445 562 581 600 513
|
||||||
|
618 515 595 530 554 553
|
||||||
|
623 514 596 532 600 509
|
||||||
|
581 1683 604 1637 601 1637
|
||||||
|
633 1636 604 502 584 1710
|
||||||
|
577 1638 555 1686 580 563
|
||||||
|
544 584 598 512 619 1638
|
||||||
|
556 563 571 563 543 592
|
||||||
|
588 510 623 1637 603 1636
|
||||||
|
556 1687 580 559 548 1682
|
||||||
|
584 1686 555 1684 556 1684
|
||||||
|
583
|
||||||
|
|
||||||
|
name 4_ORANGE
|
||||||
|
9158 4444 620 517 621 490
|
||||||
|
648 490 620 517 621 490
|
||||||
|
647 491 620 517 621 490
|
||||||
|
647 1624 620 1622 623 1622
|
||||||
|
651 1622 624 491 647 1622
|
||||||
|
623 1621 623 1622 650 491
|
||||||
|
620 518 620 1622 622 1623
|
||||||
|
648 493 619 519 619 493
|
||||||
|
621 518 593 1647 629 1644
|
||||||
|
596 517 621 516 593 1648
|
||||||
|
624 1646 598 1647 597 1647
|
||||||
|
625
|
||||||
|
|
||||||
|
name 7_YELLOW
|
||||||
|
9092 4468 593 543 592 518
|
||||||
|
619 517 592 544 593 515
|
||||||
|
621 518 592 543 592 518
|
||||||
|
620 1647 593 1650 596 1648
|
||||||
|
621 1647 596 517 619 1647
|
||||||
|
601 1642 595 1648 623 516
|
||||||
|
593 543 593 517 620 516
|
||||||
|
592 1649 621 518 591 544
|
||||||
|
594 516 619 1647 595 1662
|
||||||
|
582 1646 622 1647 596 517
|
||||||
|
618 1647 595 1648 595 1649
|
||||||
|
621
|
||||||
|
|
||||||
|
name STAR_YELLOW
|
||||||
|
9012 4468 589 544 587 518
|
||||||
|
613 518 588 543 586 519
|
||||||
|
615 516 587 544 586 522
|
||||||
|
608 1651 588 1650 588 1650
|
||||||
|
613 1650 588 520 612 1649
|
||||||
|
588 1649 588 1650 614 520
|
||||||
|
587 547 583 1649 588 543
|
||||||
|
589 1650 588 545 587 517
|
||||||
|
613 518 587 1649 614 1651
|
||||||
|
586 518 613 1650 589 517
|
||||||
|
611 1652 587 1650 588 1650
|
||||||
|
613
|
||||||
|
|
||||||
|
name 2_GREEN
|
||||||
|
9022 4497 569 566 561 546
|
||||||
|
587 546 561 573 561 547
|
||||||
|
587 547 561 571 562 546
|
||||||
|
589 1679 562 1677 564 1677
|
||||||
|
591 1678 564 546 588 1679
|
||||||
|
563 1678 564 1678 639 1631
|
||||||
|
565 546 588 546 561 1678
|
||||||
|
638 497 610 525 604 501
|
||||||
|
637 495 613 521 560 1679
|
||||||
|
563 1678 590 549 609 1626
|
||||||
|
589 1678 606 1636 561 1678
|
||||||
|
590
|
||||||
|
|
||||||
|
|
||||||
|
name 8_LIGHT_BLUE
|
||||||
|
9062 4473 589 548 587 521
|
||||||
|
615 520 586 548 588 519
|
||||||
|
614 521 586 547 587 520
|
||||||
|
615 1650 588 1653 589 1651
|
||||||
|
616 1651 588 522 612 1653
|
||||||
|
585 1655 536 1704 608 1659
|
||||||
|
537 572 561 572 533 600
|
||||||
|
546 1691 536 599 534 573
|
||||||
|
560 573 533 600 531 1706
|
||||||
|
581 1659 612 1654 586 522
|
||||||
|
609 1655 587 1653 585 1653
|
||||||
|
615
|
||||||
|
|
||||||
|
|
||||||
|
name 0_LIGHT_BLUE
|
||||||
|
9001 4491 564 569 563 543
|
||||||
|
589 542 563 569 563 543
|
||||||
|
589 542 563 569 563 542
|
||||||
|
589 1674 565 1674 565 1674
|
||||||
|
590 1674 564 543 588 1675
|
||||||
|
562 1676 535 1704 560 1703
|
||||||
|
535 572 560 1704 535 572
|
||||||
|
559 1703 535 574 557 572
|
||||||
|
533 599 533 580 552 1703
|
||||||
|
535 570 561 1703 535 572
|
||||||
|
560 1703 535 1703 535 1703
|
||||||
|
561
|
||||||
|
|
||||||
|
name 3_LIGHT_BLUE
|
||||||
|
9031 4460 596 536 596 510
|
||||||
|
622 510 595 537 594 510
|
||||||
|
622 510 618 514 593 511
|
||||||
|
621 1643 595 1643 596 1643
|
||||||
|
621 1643 595 519 612 1644
|
||||||
|
595 1643 595 1644 622 512
|
||||||
|
593 1645 620 514 591 1644
|
||||||
|
622 513 592 539 593 513
|
||||||
|
619 513 592 1644 621 513
|
||||||
|
592 1643 622 512 593 1643
|
||||||
|
622 1642 597 1642 597 1642
|
||||||
|
623
|
||||||
|
|
||||||
|
name 6_PURPLE
|
||||||
|
9015 4472 589 545 586 519
|
||||||
|
611 521 589 543 584 522
|
||||||
|
611 526 577 549 585 519
|
||||||
|
614 1650 588 1651 590 1649
|
||||||
|
614 1651 588 519 613 1649
|
||||||
|
590 1648 586 1654 613 521
|
||||||
|
584 1653 613 1652 586 1654
|
||||||
|
586 547 586 520 613 520
|
||||||
|
584 547 585 1652 588 545
|
||||||
|
585 521 616 516 586 1652
|
||||||
|
610 1655 586 1651 588 1651
|
||||||
|
614
|
||||||
|
|
||||||
|
name HASH_PINK
|
||||||
|
9048 4457 600 538 594 508
|
||||||
|
625 508 598 534 598 509
|
||||||
|
625 508 598 535 598 508
|
||||||
|
624 1638 600 1639 600 1640
|
||||||
|
627 1638 600 508 624 1641
|
||||||
|
599 1638 600 1639 626 509
|
||||||
|
597 1639 626 1640 600 509
|
||||||
|
623 1639 599 509 624 509
|
||||||
|
597 535 597 1639 600 535
|
||||||
|
597 509 623 1639 600 509
|
||||||
|
623 1639 600 1639 599 1640
|
||||||
|
626
|
||||||
|
|
||||||
|
name 5_BLUE
|
||||||
|
9065 4404 618 505 619 505
|
||||||
|
618 506 620 505 618 505
|
||||||
|
620 505 619 505 621 503
|
||||||
|
619 1623 620 1622 620 1621
|
||||||
|
620 1622 620 504 619 1623
|
||||||
|
620 1623 620 1623 617 1626
|
||||||
|
619 505 619 1623 620 1622
|
||||||
|
620 505 619 505 614 510
|
||||||
|
618 506 618 505 616 1624
|
||||||
|
619 504 620 504 619 1622
|
||||||
|
619 1623 618 1624 618 1623
|
||||||
|
617
|
||||||
|
name 9_PINK
|
||||||
|
9047 4402 616 507 618 504
|
||||||
|
619 504 618 505 618 505
|
||||||
|
615 508 618 504 619 504
|
||||||
|
618 1622 618 1622 619 1622
|
||||||
|
619 1624 618 504 618 1623
|
||||||
|
618 1623 619 1622 619 505
|
||||||
|
618 1623 619 504 618 505
|
||||||
|
620 1622 618 505 619 505
|
||||||
|
619 502 619 1622 619 506
|
||||||
|
619 1621 620 1623 618 504
|
||||||
|
619 1622 619 1622 617 1624
|
||||||
|
619
|
||||||
|
|
||||||
|
name FLASH
|
||||||
|
9066 4406 637 486 638 485
|
||||||
|
640 484 637 487 637 485
|
||||||
|
637 486 638 485 635 488
|
||||||
|
641 1602 640 1601 633 1608
|
||||||
|
634 1608 631 492 633 1609
|
||||||
|
633 1609 634 1607 631 1609
|
||||||
|
631 1610 630 493 632 1609
|
||||||
|
638 485 631 491 636 487
|
||||||
|
637 487 635 487 638 486
|
||||||
|
630 1611 638 484 632 1609
|
||||||
|
641 1599 637 1604 629 1612
|
||||||
|
638
|
||||||
|
|
||||||
|
name BRIGHTNESS_DOWN
|
||||||
|
9059 4403 619 504 619 505
|
||||||
|
619 506 618 505 618 505
|
||||||
|
619 504 618 505 619 504
|
||||||
|
620 1623 617 1624 618 1624
|
||||||
|
619 1622 619 505 619 1623
|
||||||
|
618 1623 619 1623 619 1623
|
||||||
|
619 505 619 505 619 505
|
||||||
|
617 507 619 503 619 504
|
||||||
|
619 506 616 507 616 1625
|
||||||
|
618 1624 619 1623 618 1624
|
||||||
|
618 1625 618 1623 619 1623
|
||||||
|
618
|
||||||
|
name BRIGHTNESS_UP
|
||||||
|
9055 4404 618 504 619 504
|
||||||
|
619 504 619 505 618 505
|
||||||
|
618 505 617 506 618 505
|
||||||
|
618 1623 619 1622 619 1624
|
||||||
|
618 1623 619 505 618 1622
|
||||||
|
619 1623 616 1625 619 505
|
||||||
|
618 504 619 505 619 504
|
||||||
|
616 507 618 505 619 504
|
||||||
|
618 505 618 1623 616 1627
|
||||||
|
615 1625 618 1623 619 1623
|
||||||
|
618 1624 618 1622 619 1622
|
||||||
|
618
|
||||||
|
name STROBE
|
||||||
|
9052 4403 618 504 619 504
|
||||||
|
618 506 615 507 618 504
|
||||||
|
619 504 619 504 619 504
|
||||||
|
619 1622 617 1624 619 1622
|
||||||
|
618 1623 619 504 617 1624
|
||||||
|
619 1622 619 1623 619 1622
|
||||||
|
619 1622 619 1622 618 1622
|
||||||
|
620 504 619 505 618 505
|
||||||
|
614 509 618 505 614 509
|
||||||
|
618 504 619 507 614 1625
|
||||||
|
619 1623 616 1625 619 1622
|
||||||
|
619
|
||||||
|
name FADE
|
||||||
|
9040 4403 616 504 618 504
|
||||||
|
620 503 618 504 618 505
|
||||||
|
617 505 616 504 618 504
|
||||||
|
618 1621 617 1624 614 1625
|
||||||
|
617 1621 618 504 614 1625
|
||||||
|
617 1622 618 1622 617 1623
|
||||||
|
617 1622 618 504 618 504
|
||||||
|
617 1623 617 504 615 507
|
||||||
|
616 505 618 503 618 504
|
||||||
|
618 1622 618 1622 617 505
|
||||||
|
618 1623 617 1623 617 1622
|
||||||
|
617
|
||||||
|
name SMOOTH
|
||||||
|
9040 4401 619 504 618 504
|
||||||
|
618 504 617 505 618 503
|
||||||
|
618 503 618 503 617 505
|
||||||
|
617 1623 616 1623 618 1623
|
||||||
|
616 1624 617 505 617 1622
|
||||||
|
617 1622 616 1624 618 1621
|
||||||
|
617 1623 614 1623 619 503
|
||||||
|
617 1622 618 504 618 504
|
||||||
|
617 504 618 504 617 505
|
||||||
|
617 505 616 1623 618 504
|
||||||
|
617 1623 616 1622 618 1621
|
||||||
|
618
|
||||||
|
|
||||||
|
|
||||||
|
end raw_codes
|
||||||
|
|
||||||
|
end remote
|
||||||
26
roles/pi-lirc/tasks/main.yml
Normal file
26
roles/pi-lirc/tasks/main.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
- name: Install lirc
|
||||||
|
apt:
|
||||||
|
name: lirc
|
||||||
|
- name: Install config file lirc_options.conf
|
||||||
|
copy: src=lirc_options.conf dest=/etc/lirc/lirc_options.conf
|
||||||
|
- name: Install config file lircd.conf
|
||||||
|
copy: src=lircd.conf dest=/etc/lirc/lircd.conf
|
||||||
|
- name: Install remote file
|
||||||
|
copy: src=hauppauge.conf dest=/etc/lirc/hauppauge.conf
|
||||||
|
- name: Activate overlay in boot config
|
||||||
|
lineinfile:
|
||||||
|
path: /boot/firmware/config.txt
|
||||||
|
regexp: "^#?dtoverlay=gpio-ir"
|
||||||
|
line: "dtoverlay=gpio-ir,gpio_pin=17"
|
||||||
|
register: boot_overlay
|
||||||
|
- name: Restart lircd
|
||||||
|
systemd: name=lircd state=started enabled=yes daemon_reload=yes
|
||||||
|
- name: Reboot if boot overlay changed
|
||||||
|
reboot:
|
||||||
|
when: boot_overlay.changed
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: lircd
|
||||||
40
roles/pi-musicmouse/files/config.yml
Normal file
40
roles/pi-musicmouse/files/config.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
general:
|
||||||
|
alsa_device: softvol_effects
|
||||||
|
mqtt:
|
||||||
|
user: musicmouse
|
||||||
|
password: KNLEFLZF94yA6Zhj141
|
||||||
|
server: homeassistant
|
||||||
|
hass_url: https://ha.bauer.tech
|
||||||
|
hass_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI4N2ExMzM2ZmQ4ZWQ0ZDgzOWZhMjU3NmZjYTg1NWQ1ZiIsImlhdCI6MTcwNDAyOTUyOSwiZXhwIjoyMDE5Mzg5NTI5fQ.vx9L5bTSey98uc8TwodShaEXSMr-hjXugPsNviR_fEw"
|
||||||
|
button_leds_brightness: 0.5
|
||||||
|
volume_increment: 5
|
||||||
|
min_volume: 23
|
||||||
|
max_volume: 70
|
||||||
|
serial_port: "/dev/ttyUSB0"
|
||||||
|
|
||||||
|
figures:
|
||||||
|
elefant:
|
||||||
|
id: "88041174e9"
|
||||||
|
colors: ["#ffff00", "#00c8ff", "#094b46", "#c20099"]
|
||||||
|
fuchs:
|
||||||
|
id: "8804ce7230"
|
||||||
|
colors: ["#F4D35E", "#F95738", "#F95738", "#083d77"]
|
||||||
|
eule:
|
||||||
|
id: "88040d71f0"
|
||||||
|
colors: ["#e5a200", "#f8e300", "w33", "w99"]
|
||||||
|
omnom:
|
||||||
|
id: "88043c6ede"
|
||||||
|
colors: ["#005102", "#fec800", "#005102", "#3bc405"]
|
||||||
|
eichhoernchen:
|
||||||
|
id: "88040b78ff"
|
||||||
|
colors: ["#ff0ada", "#4BC6B9", "#69045a", "#4BC6B9"]
|
||||||
|
hund:
|
||||||
|
id: "8804bc7444"
|
||||||
|
colors: ["#ffff00", "#00c8ff", "#094b46", "#c20099"]
|
||||||
|
hase:
|
||||||
|
id: "88044670ba"
|
||||||
|
colors: ["#ffff00", "#00c8ff", "#094b46", "#c20099"]
|
||||||
|
schneemann:
|
||||||
|
id: "88043f71c2"
|
||||||
|
colors: ["#ff0ada", "#4BC6B9", "#69045a", "#4BC6B9"]
|
||||||
|
|
||||||
11
roles/pi-musicmouse/files/musicmouse.service
Normal file
11
roles/pi-musicmouse/files/musicmouse.service
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=MusicMouse Player
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
ExecStart=/opt/musicmouse_venv/bin/python /opt/musicmouse/espmusicmouse/host_driver/main.py /media/musicmouse/
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
15
roles/pi-musicmouse/files/smb.conf
Normal file
15
roles/pi-musicmouse/files/smb.conf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[global]
|
||||||
|
workgroup = WORKGROUP
|
||||||
|
logging = syslog@1
|
||||||
|
server role = standalone server
|
||||||
|
obey pam restrictions = no
|
||||||
|
unix password sync = no
|
||||||
|
|
||||||
|
|
||||||
|
[MusicMouse]
|
||||||
|
browseable = yes
|
||||||
|
path = /media/musicmouse
|
||||||
|
guest ok = no
|
||||||
|
writeable = yes
|
||||||
|
create mask = 666
|
||||||
|
force create mode = 666
|
||||||
38
roles/pi-musicmouse/tasks/main.yml
Normal file
38
roles/pi-musicmouse/tasks/main.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
- name: Packages
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- python3
|
||||||
|
- python3-pip
|
||||||
|
- python3-vlc # also in venv - but this installs vlc + deps
|
||||||
|
- samba
|
||||||
|
- name: Checkout Musicmouse repo
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: 'ssh://git@git.bauer.tech:2222/martin/musicmouse.git'
|
||||||
|
dest: '/opt/musicmouse'
|
||||||
|
version: 'release/1.1'
|
||||||
|
accept_hostkey: true
|
||||||
|
- name: Create and update virtual env
|
||||||
|
ansible.builtin.pip:
|
||||||
|
requirements: /opt/musicmouse/espmusicmouse/host_driver/requirements.txt
|
||||||
|
virtualenv: /opt/musicmouse_venv
|
||||||
|
virtualenv_command: "/usr/bin/python3 -m venv"
|
||||||
|
- name: Create media directory
|
||||||
|
file:
|
||||||
|
path: /media/musicmouse
|
||||||
|
state: directory
|
||||||
|
- name: Install config file
|
||||||
|
copy: src=config.yml dest=/media/musicmouse/config.yml
|
||||||
|
- name: Install systemd service file
|
||||||
|
copy: src=musicmouse.service dest=/etc/systemd/system/
|
||||||
|
- name: Add script to autostart and start now
|
||||||
|
systemd: name=musicmouse state=restarted enabled=yes daemon_reload=yes
|
||||||
|
- name: Samba setup
|
||||||
|
copy: src=smb.conf dest=/etc/samba/
|
||||||
|
- name: Restart samba
|
||||||
|
systemd: name=smbd state=restarted enabled=yes
|
||||||
|
# manual steps:
|
||||||
|
# - set samba passwords with smbpasswd
|
||||||
|
# - copy music into /media/musicmouse (or via samba share)
|
||||||
|
# - upload host driver?
|
||||||
|
# - manual patching of hassclient necessary :( loop has to be passed in, to not use running_event_loop
|
||||||
3
roles/pi-shairport/defaults/main.yml
Normal file
3
roles/pi-shairport/defaults/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
shairport_sync_version: "3.3.5"
|
||||||
|
shairport_name: Unnamed Raspberry with shairport
|
||||||
3
roles/pi-shairport/meta/main.yml
Normal file
3
roles/pi-shairport/meta/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: pi-alsasetup
|
||||||
38
roles/pi-shairport/tasks/main.yml
Normal file
38
roles/pi-shairport/tasks/main.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
- name: Apt install dependencies
|
||||||
|
apt:
|
||||||
|
cache_valid_time: 7200
|
||||||
|
state: present
|
||||||
|
name:
|
||||||
|
- build-essential
|
||||||
|
- git
|
||||||
|
- xmltoman
|
||||||
|
- autoconf
|
||||||
|
- automake
|
||||||
|
- libtool
|
||||||
|
- libpopt-dev
|
||||||
|
- libconfig-dev
|
||||||
|
- libasound2-dev
|
||||||
|
- avahi-daemon
|
||||||
|
- libavahi-client-dev
|
||||||
|
- libssl-dev
|
||||||
|
- libsoxr-dev
|
||||||
|
- name: Build and Install Shairport sync (may take a while)
|
||||||
|
script: "build-shairport-sync.sh ${shairport_sync_version}"
|
||||||
|
args:
|
||||||
|
creates: /usr/local/bin/shairport-sync
|
||||||
|
- name: Copy config
|
||||||
|
template: src=shairport-sync.conf dest=/etc/shairport-sync.conf
|
||||||
|
- name: Modify service file to run as root
|
||||||
|
lineinfile:
|
||||||
|
path: /lib/systemd/system/shairport-sync.service
|
||||||
|
regexp: "^#?User="
|
||||||
|
line: "User=root"
|
||||||
|
- name: Restart shairport-sync
|
||||||
|
systemd: name=shairport-sync state=restarted enabled=yes daemon_reload=yes
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: shairport-sync
|
||||||
|
|
||||||
BIN
roles/pi-sispmctl/files/sispmctl-4.7.tar.gz
Normal file
BIN
roles/pi-sispmctl/files/sispmctl-4.7.tar.gz
Normal file
Binary file not shown.
36
roles/pi-sispmctl/files/sispmctl.service
Normal file
36
roles/pi-sispmctl/files/sispmctl.service
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=SiS PM Control for Linux
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
Wants=systemd-udev-settle.service
|
||||||
|
After=systemd-udev-settle.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
LockPersonality=true
|
||||||
|
MemoryDenyWriteExecute=true
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
PrivateUsers=true
|
||||||
|
ProtectClock=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
ProtectHome=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
RemoveIPC=true
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6
|
||||||
|
RestrictNamespaces=true
|
||||||
|
RestrictRealtime=true
|
||||||
|
SystemCallFilter=@system-service
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
UMask=177
|
||||||
|
|
||||||
|
Type=forking
|
||||||
|
ExecStart=/usr/bin/sispmctl -p 2638 -l
|
||||||
|
SyslogIdentifier=sispmctl
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
21
roles/pi-sispmctl/tasks/main.yml
Normal file
21
roles/pi-sispmctl/tasks/main.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
- name: Check if sispmctl already exists
|
||||||
|
stat: path=/usr/bin/sispmctl
|
||||||
|
register: sispmctl_file
|
||||||
|
- name: Install dependencies
|
||||||
|
apt: name="libusb-dev" cache_valid_time=7200 state=present
|
||||||
|
- name: Copy sispmctl sources
|
||||||
|
unarchive: src=sispmctl-4.7.tar.gz dest=/tmp
|
||||||
|
when: sispmctl_file.stat.exists == false
|
||||||
|
- name: Build and install
|
||||||
|
shell: cd /tmp/sispmctl*/ && ./configure --prefix=/usr && make install
|
||||||
|
when: sispmctl_file.stat.exists == false
|
||||||
|
- name: Install systemd service file
|
||||||
|
copy: src=sispmctl.service dest=/lib/systemd/system/
|
||||||
|
- name: Add script to autostart and start now
|
||||||
|
systemd: name=sispmctl state=started enabled=yes daemon_reload=yes
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: sispmctl
|
||||||
47
roles/pi-squeezelite-custom/custom build/readme_build.md
Normal file
47
roles/pi-squeezelite-custom/custom build/readme_build.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
Repository: https://github.com/ralph-irving/squeezelite.git
|
||||||
|
Version: c89faf3280c5dca3f04906a4ff7796a0043e99f0
|
||||||
|
|
||||||
|
|
||||||
|
Packages to build:
|
||||||
|
- libmad0
|
||||||
|
- libmpg123-0
|
||||||
|
- libflac8
|
||||||
|
- libvorbisfile3
|
||||||
|
- libfaad2
|
||||||
|
# for building libssl-dev, libasound2-dev, libflac-dev, libvorbis-dev, libsoxr-dev, libfaad-dev, libmad0-dev, libmpg123-dev
|
||||||
|
|
||||||
|
Adapted makefile.rpi
|
||||||
|
#OPTS = -DRESAMPLE -DDSD -DUSE_SSL -DLINKALL -I./include -I./include/opus -I./include/alac -I/usr/local/include -s -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Old
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
Repository: https://github.com/ralph-irving/squeezelite.git
|
||||||
|
Version: 779fe9035a2dfcaeeb5335497a012d5e7241a409
|
||||||
|
|
||||||
|
Custom Makefile based on Makefile.rpi:
|
||||||
|
|
||||||
|
```Makefile
|
||||||
|
OPTS = -DLINUX -DUSE_SSL -DLINKALL -I./include -I./include/opus -I./include/alac -I/usr/local/include -s -march=armv6 -mfloat-abi=hard -mfpu=vfp
|
||||||
|
|
||||||
|
LDFLAGS=-L./lib -L/usr/local/lib -s -lgomp -lasound
|
||||||
|
|
||||||
|
include Makefile
|
||||||
|
```
|
||||||
|
|
||||||
|
Required packages:
|
||||||
|
```bash
|
||||||
|
apt-get install libasound2 libasound2-dev libflac-dev libmad0-dev libfaad-dev libmpg123-dev libvorbis-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
- no RPI switch (this is only when wanting to do switiching based on GPIO headers)
|
||||||
|
- manually added -lasound (don't know why it wasn't in there)
|
||||||
|
|
||||||
|
Then just build with
|
||||||
|
make -f mymakefile (on a raspi)
|
||||||
|
|
||||||
BIN
roles/pi-squeezelite-custom/files/squeezelite
Executable file
BIN
roles/pi-squeezelite-custom/files/squeezelite
Executable file
Binary file not shown.
BIN
roles/pi-squeezelite-custom/files/squeezelite_old
Executable file
BIN
roles/pi-squeezelite-custom/files/squeezelite_old
Executable file
Binary file not shown.
31
roles/pi-squeezelite-custom/tasks/main.yml
Normal file
31
roles/pi-squeezelite-custom/tasks/main.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
- name: Uninstall system package of squeezelite
|
||||||
|
apt: name=squeezelite state=absent
|
||||||
|
- name: Install dependencies
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- libmad0
|
||||||
|
- libmpg123-0
|
||||||
|
- libflac12
|
||||||
|
- libvorbisfile3
|
||||||
|
- libfaad2
|
||||||
|
# for building libssl-dev, libasound2-dev, libflac-dev, libvorbis-dev, libsoxr-dev, libfaad-dev, libmad0-dev, libmpg123-dev
|
||||||
|
state: present
|
||||||
|
cache_valid_time: 7200
|
||||||
|
- name: Remove old config file if present
|
||||||
|
file: path=/etc/default/squeezelite state=absent
|
||||||
|
- name: Copy over custom compile version of squeezelite
|
||||||
|
copy: src=squeezelite dest=/opt/squeezelite mode=700
|
||||||
|
- name: Install systemd service file
|
||||||
|
template: src=squeezelite.service dest=/lib/systemd/system/
|
||||||
|
- name: Enable sysdweb autostart
|
||||||
|
systemd: name=squeezelite state=restarted enabled=yes daemon_reload=yes
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: squeezelite
|
||||||
|
|
||||||
|
|
||||||
|
# build with adapted Makefile.rpi:
|
||||||
|
#OPTS = -DRESAMPLE -DDSD -DUSE_SSL -DLINKALL -I./include -I./include/opus -I./include/alac -I/usr/local/include -s -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
|
||||||
10
roles/pi-squeezelite-custom/templates/squeezelite.service
Normal file
10
roles/pi-squeezelite-custom/templates/squeezelite.service
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Squeezelite
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/opt/squeezelite -n {{squeezelite_name}} -s {{squeezeserver}} -o softvol_squeezelite
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
2
roles/pi-squeezelite/defaults/main.yml
Normal file
2
roles/pi-squeezelite/defaults/main.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
squeezeserver: 192.168.178.80
|
||||||
3
roles/pi-squeezelite/handlers/main.yml
Normal file
3
roles/pi-squeezelite/handlers/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
- name: restart-squeezelite
|
||||||
|
systemd: name=squeezelite state=restarted enabled=yes daemon_reload=yes
|
||||||
3
roles/pi-squeezelite/meta/main.yml
Normal file
3
roles/pi-squeezelite/meta/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: pi-alsasetup
|
||||||
13
roles/pi-squeezelite/tasks/main.yml
Normal file
13
roles/pi-squeezelite/tasks/main.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
- name: Apt install squeezelite package
|
||||||
|
apt: name=squeezelite cache_valid_time=7200 state=present
|
||||||
|
notify: restart-squeezelite
|
||||||
|
- name: Install config file
|
||||||
|
template: src=squeezelite.cfg dest=/etc/default/squeezelite
|
||||||
|
notify: restart-squeezelite
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: squeezelite
|
||||||
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
SL_NAME="{{squeezelite_name}}"
|
SL_NAME="{{squeezelite_name}}"
|
||||||
SL_SOUNDCARD="softvol_squeezelite"
|
SL_SOUNDCARD="softvol_squeezelite"
|
||||||
SB_SERVER_IP="192.168.178.80"
|
SB_SERVER_IP="{{squeezeserver}}"
|
||||||
BIN
roles/pi-squeezeserver/files/logitechmediaserver_7.9.2_arm.deb
Normal file
BIN
roles/pi-squeezeserver/files/logitechmediaserver_7.9.2_arm.deb
Normal file
Binary file not shown.
BIN
roles/pi-squeezeserver/files/logitechmediaserver_8.4.0_arm.deb
Normal file
BIN
roles/pi-squeezeserver/files/logitechmediaserver_8.4.0_arm.deb
Normal file
Binary file not shown.
2
roles/pi-squeezeserver/files/source_url.txt
Normal file
2
roles/pi-squeezeserver/files/source_url.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#http://downloads.slimdevices.com/LogitechMediaServer_v7.9.2/logitechmediaserver_7.9.2_arm.deb
|
||||||
|
https://downloads.slimdevices.com/LogitechMediaServer_v8.4.0/logitechmediaserver_8.4.0_arm.deb
|
||||||
39
roles/pi-squeezeserver/tasks/main.yml
Normal file
39
roles/pi-squeezeserver/tasks/main.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
- name: Install packages required for squeeze server
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- libsox-fmt-all
|
||||||
|
- libflac-dev
|
||||||
|
- libfaad2
|
||||||
|
- libmad0
|
||||||
|
- perl-openssl-defaults
|
||||||
|
- libnet-ssleay-perl
|
||||||
|
- libio-socket-ssl-perl
|
||||||
|
- nasm
|
||||||
|
- build-essential
|
||||||
|
- iptables-persistent
|
||||||
|
cache_valid_time: 7200
|
||||||
|
state: present
|
||||||
|
- name: copy squeezeserver package
|
||||||
|
copy: src=logitechmediaserver_8.4.0_arm.deb dest=/tmp
|
||||||
|
- name: install squeezeserver package
|
||||||
|
apt: deb=/tmp/logitechmediaserver_8.4.0_arm.deb
|
||||||
|
- name: Enable sysdweb autostart
|
||||||
|
systemd: name=logitechmediaserver state=started enabled=yes
|
||||||
|
- name: Add to sysdweb
|
||||||
|
include_role:
|
||||||
|
name: pi-sysdweb
|
||||||
|
vars:
|
||||||
|
sysdweb_name: logitechmediaserver
|
||||||
|
- name: Forward port 80 to 9000
|
||||||
|
iptables:
|
||||||
|
table: nat
|
||||||
|
chain: PREROUTING
|
||||||
|
in_interface: eth0
|
||||||
|
protocol: tcp
|
||||||
|
match: tcp
|
||||||
|
destination_port: "80"
|
||||||
|
jump: REDIRECT
|
||||||
|
to_ports: "9000"
|
||||||
|
comment: Redirect web traffic to port 9000
|
||||||
|
|
||||||
13
roles/pi-standard-setup/defaults/main.yml
Normal file
13
roles/pi-standard-setup/defaults/main.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
wifi_ssid: "" # put SSID here to configure wifi
|
||||||
|
ansible_user: "root" # "User to connect with, put in 'pi' here if you connect the first time, else leave empty"
|
||||||
|
new_hostname: "" # set this to change the hostname
|
||||||
|
|
||||||
|
|
||||||
|
timezone: "Europe/Berlin"
|
||||||
|
wifi_country: "DE"
|
||||||
|
wifi_pass_url: "bauer_wifi" # has to be in keepass with url "wifi_pass_url"
|
||||||
|
ansible_ssh_pass: "raspberry"
|
||||||
|
ansible_become_password: "raspberry"
|
||||||
|
ansible_become: yes
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user