Category Archives: IOT / Domoticz

LMS Record player V.something .. final? No

But it works! Many iterations .. almost perfect

Recordplayer model by kriswillcode, but heavily remixed

Record player is going to be re-printed at a higher quality.

  • Put a printed image on the player, and it plays the album
  • Move the arm, and the next track will be played
  • Press upper white button, and the music will pause/resume
  • Press lower button … ??? Don’t know yet

Updated python client (see previous posts)

import paho.mqtt.client as mqtt
import urllib.request
from time import sleep

def on_connect(client, userdata, flags, rc):  # The callback for when the client connects to the broker 
        print("Connected with result code {0}".format(str(rc)))  
        client.subscribe("spotify/rfid/idlms")  
        client.subscribe("spotify/rfid/but1")  
        client.subscribe("spotify/rfid/but2")  
        client.subscribe("spotify/rfid/arm")  

def on_message(client, userdata, msg):  # The callback for when a PUBLISH message is received from the server. 
        print("Message received-> " + msg.topic + " " + str(msg.payload))  # Print a received msg
        if msg.topic == "spotify/rfid/idlms":
            urllib.request.urlopen("http://LMS-SERVER-IP:9000/anyurl?p0=playlistcontrol&p1=album_id:" + msg.payload.decode() + "&p2=cmd:load&player=00:04:20:16:d9:04")
        if msg.topic == "spotify/rfid/but1":
            urllib.request.urlopen("http://LMS-SERVER-IP:9000/anyurl?p0=pause&player=00:04:20:16:d9:04")
            sleep(1)
        if msg.topic == "spotify/rfid/but2":
            urllib.request.urlopen("http://LMS-SERVER-IP:9000/anyurl?p0=pause&pt=1&player=00:04:20:16:d9:04")
            sleep(1)
        if msg.topic == "spotify/rfid/arm":
            urllib.request.urlopen("http://LMS-SERVER-IP:9000/status.html?p0=button&p1=jump_fwd&player=00:04:20:16:d9:04")
            sleep(1)

client = mqtt.Client("lmsclient")  # Create instance of client with client ID β€œdigi_mqtt_test”
client.on_connect = on_connect  # Define callback function for successful connection
client.on_message = on_message  # Define callback function for receipt of a message
client.connect('MQTTSERVER', 1883)
client.loop_forever()  # Start daemon

Wemos INO file

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>
#define SS_PIN 15
#define RST_PIN 0

const int buttonPin1 = D1;  
const int buttonPin2 = D2;   

int buttonState1 = 0; 
int buttonState2 = 0; 

MFRC522 mfrc522(SS_PIN, RST_PIN);
  MFRC522::StatusCode status; //variable to get card status
  
  byte buffer[18];  //data transfer buffer (16+2 bytes data+CRC)
  byte size = sizeof(buffer);

  uint8_t pageAddr = 0x06;  //In this example we will write/read 16 bytes (page 6,7,8 and 9).
                            //Ultraligth mem = 16 pages. 4 bytes per page.  
                            //Pages 0 to 4 are for special functions.           
  
unsigned long cardId = 0;
WiFiClient net;
PubSubClient client(net);
const char* mqtt_server = "MQTTBROKER";
const char* ssid = "MYSSID";
const char* password = "MYWIFIPASWORD";
String topicStr = "";
byte buffer2[8];

boolean Rflag=false;
int r_len;
char payload[5];
byte value[5];
void setup() {
  Serial.begin(9600);
    pinMode(buttonPin1, INPUT_PULLUP);
    pinMode(buttonPin2, INPUT_PULLUP  );

  SPI.begin();
  mfrc522.PCD_Init();
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(ssid, password);
  client.setServer(mqtt_server, 1883);
     delay(100);
    client.setCallback(callback);
      delay(100);
    client.subscribe("spotify/rfid/in/#");
}
void reconnect() {
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
  }
  while (!client.connected()) {
    String clientId = "rfid-";
    clientId += String(random(0xffff), HEX);
    if (!client.connect(clientId.c_str(), "rfidclient", "...")) {
      delay(5000);
    }
  }
  client.subscribe("spotify/rfid/in/#");
}
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print(F("Called"));
   Rflag=true; //will use in main loop
   r_len=length; //will use in main loop
   int j=0;
     for (j;j<length;j++) {
       buffer2[j]=payload[j];
       //Serial.print((char)payload[j]);
       }
if (r_len < 3) {
  Rflag=false;
  Serial.print(F("Set false"));
}
buffer2[j]='\0'; //terminate string
}

void loop() {
    if (!client.connected()) {
    reconnect();
  }

buttonState1 = digitalRead(buttonPin1);
  //Serial.print(buttonState1);
  if (buttonState1 == 0 ) {
    client.publish("spotify/rfid/but1", "0");
  }
buttonState2 = digitalRead(buttonPin2);
  //Serial.println(buttonState2);
  if (buttonState2 == 0 ) {
    client.publish("spotify/rfid/but2", "0");
  }

int reading = analogRead(0);
  //Serial.println(reading);
  if (reading > 500 ) {
    client.publish("spotify/rfid/arm", "0");
  }
  
  client.loop();
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }
if (Rflag) {
        for (int i=0; i < 4; i++) {
    //data is writen in blocks of 4 bytes (4 bytes per page)
    status = (MFRC522::StatusCode) mfrc522.MIFARE_Ultralight_Write(pageAddr+i, &buffer2[i*4], 4);
    if (status != MFRC522::STATUS_OK) {
      return;
    }
    
  }
  Rflag=false;
}

  cardId = getCardId();
  char buffer3[10];
  sprintf(buffer3, "%lu", cardId);
  client.publish("spotify/rfid/id", buffer3);

  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(pageAddr, buffer, &size);
  if (status != MFRC522::STATUS_OK) {
    Serial.println(F("MIFARE_Read() failed: (R)"));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  Serial.println(F("Read data: "));
  for (byte i = 0; i < 5; i++) {
    Serial.write(buffer[i]);
       buffer2[i]=buffer[i];
    }
       
  client.publish("spotify/rfid/idlms", buffer,5);
  delay(1000);
  mfrc522.PICC_HaltA();
}

unsigned long getCardId() {
  byte readCard[4];
  for (int i = 0; i < 4; i++) {
    readCard[i] = mfrc522.uid.uidByte[i];
  }
  return (unsigned long)readCard[0] << 24
    | (unsigned long)readCard[1] << 16
    | (unsigned long)readCard[2] << 8
    | (unsigned long)readCard[3];
}

64×64 Etch a Sketch

In the past I made a Etch a Sketch with my lasercutter.

Using two rotary encoders and the 64×64 matrix display I recently bought, I made a drawing thingy.
Like a Etch a Sketch.

Some Circuit Python code.
Now I have to fix an out of memory issue using below.
And make a colour selection button??? πŸ™‚

import time
import board
import displayio
import math
import vectorio
import rgbmatrix
import framebufferio
import array
import bitmaptools

import rotaryio
import board

encoder1 = rotaryio.IncrementalEncoder(board.GP27, board.GP26)
encoder2 = rotaryio.IncrementalEncoder(board.GP18, board.GP19)

last_position1 = 0
last_position2 = 0

# Release any existing displays
displayio.release_displays()

# --- Matrix Properties ---
DISPLAY_WIDTH = 64
DISPLAY_HEIGHT = 64

# --- Matrix setup ---
BIT_DEPTH = 2
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=2, height=64,
    rgb_pins=[board.GP0, board.GP1, board.GP2, board.GP3, board.GP4, board.GP5],
    addr_pins=[board.GP6, board.GP7, board.GP8, board.GP9, board.GP22],
    clock_pin=board.GP10, latch_pin=board.GP12, output_enable_pin=board.GP13)
colrs = 13
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=True)
b1 = displayio.Bitmap(display.width, display.height, colrs )
palette = displayio.Palette(colrs )
palette[0] = 0x000000  # black
palette[1] = 0x964B00  # brown (light yellow) 
palette[2] = 0x00FFFF  # cyan
palette[3] = 0x850101  # deep red 
palette[4] = 0x7F00FF  # violet
palette[5] = 0xC46210  # orange
palette[6] = 0x3D9140  # Cobalt green  
palette[7] = 0x004225  # british racing green 
palette[8] = 0x8B008B  # dark magenta 
palette[9] = 0x1F75FE  # crayola  blue
palette[10] =0x00308F  # air force blue US air force    
palette[11] =0xBF00FF  # electric purple 
palette[12] =0x08E8DE  # turquoise
g1 = displayio.Group(scale=1)
display.root_group = g1 

bmp = displayio.Bitmap(64,64, 2)

tilegrid = displayio.TileGrid(bitmap=bmp, pixel_shader=palette)
g1.append(tilegrid)
display.auto_refresh = True

tilegrid = displayio.TileGrid(bitmap=bmp, pixel_shader=palette)
while True:
        position1 = encoder1.position
        if last_position1 is None or position1 != last_position1:

            if position1 > last_position1:
                position1 = position1 + 1
            if position1 < last_position1:
                position1 = position1 - 1
            if position1 < 0:
                position1 = 0
            last_position1 = position1
        position2 = encoder2.position
        if last_position2 is None or position2 != last_position2:
            if position2 > last_position2:
                position2 = position2 + 1
            if position2 > last_position2:
                position2 = position2 - 1
            if position2 < 0:
                position2 = 0
            last_position2 = position2

        bmp[position1,position2]=1
        tilegrid = displayio.TileGrid(bitmap=bmp, pixel_shader=palette)
        g1.append(tilegrid)
        display.auto_refresh = True

Media control automation in HA revisited

I was not happy with my old setup to control all media devices in my living room.

Controlling all devices using a single button and automations was not easy.

Problems encountered :

  1. Switching from one device to another toggled a device used by both.
  2. Switching from TV to Spotify (using my Onkyo), turned off the TV and cec turned off the Onkyo
  3. Using the Home Assistant Onkyo integration, I could select NET but not NET>Spotify
  4. The Harmony hub I’m using for Infrared Control didn’t expose all buttons.
  5. My Set-top box only power toggles. No way of reading the state.

Addressing problems:

Number 1 & 2 : Disable CEC, and manual add all that cec tried to do for me. (https://en.wikipedia.org/wiki/Consumer_Electronics_Control)

Number 5 :
(See post : https://www.henriaanstoot.nl/2024/04/01/multipurpose-rgb-sensor-esphome/)

Number 3 & 4 : Two options, integrate my web controller for onkyo.
(https://www.henriaanstoot.nl/2009/10/23/onkyo-web-control-hack/)
Or give HA more functionality.

Lets add an Onkyo python script to HA.
Open a webterminal and enter following command

pip install onkyo-eiscp

This will give you:
(Hint: want to copy text from the web terminal? Hold shift while selecting text)

➜  ~ pip install onkyo-eiscp   
Looking in indexes: https://pypi.org/simple, https://wheels.home-assistant.io/musllinux-index/
Collecting onkyo-eiscp
  Downloading https://wheels.home-assistant.io/musllinux-index/onkyo_eiscp-1.2.7-py3-none-any.whl.metadata (9.9 kB)
Collecting docopt>=0.4.1 (from onkyo-eiscp)
  Downloading https://wheels.home-assistant.io/musllinux-index/docopt-0.6.2-py2.py3-none-any.whl.metadata (17 kB)
Collecting netifaces (from onkyo-eiscp)
  Downloading https://wheels.home-assistant.io/musllinux-index/netifaces-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl.metadata (8.9 kB)
Downloading https://wheels.home-assistant.io/musllinux-index/onkyo_eiscp-1.2.7-py3-none-any.whl (45 kB)
   ━━━━━━━━━ 45.7/45.7 27.8     eta 0:00:00
             kB        MB/s                
Downloading https://wheels.home-assistant.io/musllinux-index/docopt-0.6.2-py2.py3-none-any.whl (13 kB)
Downloading https://wheels.home-assistant.io/musllinux-index/netifaces-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl (13 kB)
Installing collected packages: netifaces, docopt, onkyo-eiscp
Successfully installed docopt-0.6.2 netifaces-0.11.0 onkyo-eiscp-1.2.7
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
➜  ~ onkyo
Usage:
  onkyo [--host <host>] [--port <port>]
        [--all] [--name <name>] [--id <identifier>]
        [--verbose | -v]... [--quiet | -q]... <command>...
  onkyo --discover
  onkyo --help-commands [<zone> <command>]
  onkyo -h | --help
➜  ~ which onkyo
/usr/bin/onkyo

Now we can integrate shell commands in HA!

NET > Spotify
onkyo --host 192.168.1.2 NSV0A0

Spotify > track info
onkyo --host 192.168.1.2 NTIQSTN

Spotify > Artist
onkyo --host 192.168.1.2 NATQSTN

Turn Onkyo ON/OFF
onkyo --host 192.168.1.2 PWR01
onkyo --host 192.168.1.2 PWR00

Get Power state
onkyo --host 192.168.1.2 PWRQSTN

Mute on/off
onkyo --host 192.168.1.2 AMT01 or AMT00

Volume Level
onkyo --host 192.168.1.2 MVLQSTN

Home Assistant Shell Command example

  - platform: command_line
    switches:
      onkyo_mute:
        command_on: "/usr/bin/onkyo --host 192.168.1.2 AMT00"
        command_off: "/usr/bin/onkyo --host 192.168.1.2 AMT01"

Getting album art:

Get image from below url.
Note! .. its a static non refreshing image!

http://192.168.1.2/album_art.cgi

Note: This gives me a file which shows fine in a browser, but being a http feed you cannot embed this in a https site.
Another ‘problem’ with this image is that it includes headers, rendering this a invalid jpg when processing!
(remove first 3 lines programmatically)
I also want to have it updated, so I could make a mjpeg streamer for this.
Probably i’m going to use my reverse proxy which uses SSL offloading to access cover art over https.
(Then I can push this cover image on this webblog so you guys can see what we are listing to)

Another idea is to push this to an arduino display.

Not using https?

You can embed the album art in a dashboard. This won’t refresh, but the added time code will stop caching problems.

type: picture<br>image: http://192.168.1.2/album_art.cgi?v={{now().timestamp()|int }}")

Todo:

Make the automations foolproof.

Watching TV and want to listen to Spotify?

  1. Turn TV off
  2. Switch Onkyo to Net and Spotify
  3. (Adjust volume)
  4. Toggle TV set-top box off

etc

Next todo:
Cover art embed in HA.
Maybe in embed in this blog.
Make a small cover art display.

PHP code to strip lines and put behind a reverse proxy

<?php
header('Content-type: image/jpg');
$lines = file_get_contents('http://192.168.1.2/album_art.cgi', false);
$lines = explode("\n", $lines);
$content = implode("\n", array_slice($lines, 3));
print $content;
?>

You can use Generic Camera to refesh!

https://www.home-assistant.io/integrations/generic

Current Cover Art

Get cover art from Logitech Media Server

http://192.168.1.3:9000/music/current/cover.jpg?player=<playerid>

Arduino Tiny Machine Learning Kit

A while ago I bought a little machine learning kit.

I’ve been reading at listening to ML podcasts and websites.

One on Spotify I liked was:

Also, the following Coursera was interesting
https://www.coursera.org/learn/machine-learning

I’ve been testing using Python on my Laptop.
(see other posts)

And a camera with esp32 using face detection.

See here multiple posts about these experiments.

https://www.henriaanstoot.nl/tag/machinelearning/

Today the first experiments using this kit.

  • Arduino Nano 33 BLE Sense board
  • OV7675 Camera
  • Arduino Tiny Machine Learning Shield
  • USB A to Micro USB Cable
  • 9 axis inertial sensor: what makes this board ideal for wearable devices
  • humidity, and temperature sensor: to get highly accurate measurements of the environmental conditions
  • barometric sensor: you could make a simple weather station
  • microphone: to capture and analyse sound in real time
  • gesture, proximity, light color and light intensity sensor : estimate the room’s luminosity, but also whether someone is moving close to the board
  • Microcontroller nRF52840
  • Operating Voltage 3.3V
  • Input Voltage (limit) 21V
  • DC Current per I/O Pin 15 mA
  • Clock Speed 64MHz
  • CPU Flash Memory 1MB (nRF52840)
  • SRAM 256KB (nRF52840)
  • EEPROM none
  • Digital Input / Output Pins 14
  • PWM Pins all digital pins
  • UART 1
  • SPI 1
  • I2C 1
  • Analog Input Pins 8 (ADC 12 bit 200 ksamples)
  • Analog Output Pins Only through PWM (no DAC)
  • External Interrupts all digital pins
  • LED_BUILTIN 13
  • USB Native in the nRF52840 Processor
  • IMU LSM9DS1 (datasheet)
Gesture test ( yes on a windows surface tablet, but Vincent and I installed linux on it!)

I just started and will update this page, with other experiments.

Note: displaying Arduino output without installing the IDE

stty -F /dev/ttyACM0 raw 9600
cat /dev/ttyACM0
................................
................................
................................
................................
................................
................................
................................
................................
................................
................####............
...............##..#............
..............##...##...........
..............#.....#...........
..............###...#...........
..............##.....#..........
..............##.....#..........
...............#....##..........
...............######...........
................................
................................
................................
................................

Home Assistant Alarm with ESP Siren

I used to have a “professional” alarm system, but it was too limited.

But when making a new alarm system using Home Assistant I thought I could reuse some sensors and the very loud alarm.

Setting up the Alarm within HA was as described on the HA website.
I made a group for door and motion sensors.
Then I made groups for lighting and switches.

Now I can “ARM” the house.

  • Motion sensors like PIR and camera sensors are being used for detection.
  • Lights and sound will be turned on when motion is detected.
  • When arming the system, the siren mode of the camera’s is also turned on.
  • When intrusion is detected I get a pushover notification on my phone and watch.

The siren is about 4-5 Euro’s on Ali
https://nl.aliexpress.com/item/1005006066524139.html

Schematic of the wemos controller

(I don’t have a Siren Fritzing part .. hence the speaker)

Used mosfet is a N-Channel 30N06L, resistor is 10K

ESPHome code

esphome:
  name: bigalarm
  friendly_name: BigAlarm

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "1xxfIYKv6tpzt7HQKYOxxxxxxxxTBETHkmy7cwDE="

ota:
  password: "5d23a3af438fe0xxxxxxxx2ff29ab6"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Bigalarm Fallback Hotspot"
    password: "6muixxxxxoA"

captive_portal:

output:
  - platform: gpio
    pin: 0
    id: 'generic_out'
switch:
  - platform: output
    name: "BigAlarm"
    output: 'generic_out'

Today some lasercutting for Home Assistant Spotify RFID

see:

Lasercutting a case and the playlist selectors.

Close-up RFID stickers I’m using.

Below is a test with different methods.
I like reading the booklets, so a CD i cool, and I don’t need a CD player.
(The RFID tag is in the case)
The little cards are for bought audio files I don’t have a physical CD for.

Wooden case with RFID reader being powered by external powerbank

What am I gonna do?
Cube as I had? Wooden playlist selectors as in above movies?
The cards I’ve printed?
Maybe a small record player with an RFID reader inside?

3D printed like this? https://makerworld.com/en/models/66671
UPDATE: 20240327 – Little Record I 3D printed with little groves.

Home Assistant code for Playlist and Album automations
(B.t.w. The method is still using an Arduino and MQTT topics, as mentioned before)

# ALBUM PLAYER
alias: SpotifyAlbum
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_fashice
    data:
      media_content_type: album
      media_content_id: spotify:album:{{ playlistkeys.get(trigger.payload) }}
mode: single
variables:
  playlistkeys:
    "71719674": 20TANs4iXVeLp387zjgmec
    "71260666": 5325ECcBhnIysoqyENGCYi
    "71457530": 7wyOeD9HcUuMFMO8pTflap
# PLAYLIST PLAYER
alias: SpotifyCube
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_fashice
    data:
      media_content_type: playlist
      media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }}
variables:
  playlistkeys:
    "69229050": 0SOay3RkjojjevrF5lHMON
    "69491194": 5f8w3UHlD9Ozz6Y4VHs6kF
    "69753338": 0bJvpsn0TDZwIDUjz4d75S
    "70015482": 37i9dQZF1DX9HwI3Crikcm
    "70277626": 37i9dQZF1EQmK1rjZuPGDt
    "70539770": 2KeRLMmGMxI5UgzE7m0iCp

In the past, Aloha and I made a simple solution like this using barcodes in < 2000s.
Due to the many obscure recordings I have, I am thinking about creating something like this for Picore player and my local Squeezebox server.

Logitech Squeezebox / Media Server Solution

alias: squeezealbumplay
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: squeezebox.call_method
    target:
      entity_id: media_player.squeezebox
    data:
      command: playlist
      parameters:
        - play
        - "{{ playlistkeys.get(trigger.payload) }}"
mode: single
variables:
  playlistkeys:
    "71719674": /tank/celtic/Celtic/M/Martyn Bennett/Bothy Culture/
    "71719675": /tank/celtic/Celtic/D/Davy Spillane/Atlantic Bridge/
    "2159056458": /tank/celtic/Celtic/M/Michael McGoldrick/Arc/

Several things in progress, help me if you can.

I used MCE to control some Windows VMs and programs running in it in the past. (Below link and a web interface engine which on the backend converted BWW/BMW (bagpipe music files) to PDF automated comes to mind)

Now, I implemented this:

https://iotlink.gitlab.io/

Controlling a Windows VM using MQTT, very nice!
(Use HA mqtt or mosquitto_pub in bash)

Question: anyone got a better solution to control programs within a VM? Let me know.

Next:

I’m creating a new case for my Wemos, LCD16x2, button, Led, Buzzer project (see other post)

I’m redesigning my previous case in blender.

But I really miss something like a generator function for different cases, like the one I made using Openscad.
Question: Anyone know a tool/add-on to generate cases?
I used a model of a wemos to get the usb connector/screw holes in place.

My spotify rfid case I will create using wooden lasercut cutouts.
https://www.henriaanstoot.nl/2024/03/06/revisiting-the-spotify-cube/

Next one:

In the past, I’ve controlled some blender lights using python and MQTT. But now I’m trying to control it using DMX.

Example of lighting in our living using mock-up couch and tables.

I found a cool add-on called Blender-DMX.
(B.t.w. wled can also use DMX)

Looks cool but, can I make a floorplan with this?

Blender add-on configuration

In Home Assistant I used a HACS add-on called : Art-net LED Lighting for DMX

Configuration can be done in configuration.yaml

light:
- platform: artnet_led
  host: BLENDERHOSTIP                   # IP of Art-Net Node
  max_fps: 25                           
  refresh_every: 0                      # Resend values if no fades are running every x seconds, 0 disables automatic refresh
  node_type: artnet-direct              # Which protocol to use
  universes:                            # Support for multiple universes
    1:                                  # .Nr of Universe (see configuration of your Art-Net Node)
      send_partial_universe: True       # Only send the universe which contains data
      devices:
        - channel: 1                    # first channel of dmx dimmer
          name: dmx_dimmer_rgbw         # name
          type: rgbw                    # type
          transition: 1                 # default duration of fades in sec. 
          channel_size: 8bit            # width of the channel sent to DMX device, default "8bit", "16bit", "24bit" and "32bit" 
          channel_setup: Wrgb           # This is the magic to get colors correct

It works, but I’m not happy, anyone got a better solution?

And I have to check out GDTF profiles for fixtures.

At a later stage I’m going to 3d print a white floorplan about 1cm high, with LEDs and buttons. A floorplan you can hang on your wall.

Revisiting the Spotify Cube

In the past I posted about my genre selector for Spotify using a cube.

UPDATE: 20240501 below – using esphome

Most was done using NodeRed and a python script.

Now, I’ve moved it to Home Assistant using a single automation.
(Maybe the Arduino sketch can be made with Esphome also.
But I don’t have time for that)
It still uses the Arduino sketch as before, which uses Mqtt to post the RFID code to Mosquitto.

My new Home Assistant automation

alias: SpotifyCube
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_fash
    data:
      media_content_type: playlist
      media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }}
variables:
  playlistkeys:
    "70539770": 2KeRLMmGMxI5UgzE7m0iCx
    "70277626": 37i9dQZF1EQmK1rjZuPGDx
    "69229050": 0SOay3RkjojjevrF5lHMOx
    "70015482": 37i9dQZF1DX9HwI3Crikcx
    "69753338": 0bJvpsn0TDZwIDUjz4d75x
    "69491194": 5f8w3UHlD9Ozz6Y4VHs6kx

Some notes about above script:

  • The MQTT topic is configured in the sketch below
  • The playlist keys are at the bottom
    “RFIDID”: playliststring as can be found in web spotify

Pasted link

Arduino Code

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>

#define SS_PIN 15
#define RST_PIN 0

MFRC522 mfrc522(SS_PIN, RST_PIN);
unsigned long cardId = 0;

WiFiClient net;
PubSubClient client(net);
const char* mqtt_server = "IPMQTTBROKER";
const char* ssid = "MYSSID";
const char* password = "MYWIFIPASSWORD";

void setup() {

  Serial.begin(115200);
  SPI.begin();
  mfrc522.PCD_Init();
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(ssid, password);

  client.setServer(mqtt_server, 1883);
     delay(100);
    client.setCallback(callback);
      delay(100);
    client.subscribe("spotify/rfid/in/#");
}

void reconnect() {
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
  }

  while (!client.connected()) {
    String clientId = "NodeMCUClient-";
    clientId += String(random(0xffff), HEX);

    if (!client.connect(clientId.c_str(), "rfidclient", "...")) {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      delay(5000);
    }

  }
  client.subscribe("spotify/rfid/in/#");
}

void callback(char* topic, byte* payload, unsigned int length) {
    String topicStr = topic;
      byte value = atoi((char*)payload);
}

void loop() {
    if (!client.connected()) {
    reconnect();
  }
  client.loop();

  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  cardId = getCardId();
  char buffer[10];
  sprintf(buffer, "%lu", cardId);
  client.publish("spotify/rfid/id", buffer);

  uint8_t control = 0x00;
  do {
    control = 0;
    for (int i = 0; i < 3; i++) {
      if (!mfrc522.PICC_IsNewCardPresent()) {
        if (mfrc522.PICC_ReadCardSerial()) {
          control |= 0x16;
        }
        if (mfrc522.PICC_ReadCardSerial()) {
          control |= 0x16;
        }
        control += 0x1;
      }
      control += 0x4;
    }

    delay(0);
  } while (control == 13 || control == 14);

  reconnect();
  client.publish("spotify/rfid/id", "0");
  delay(500);

  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

unsigned long getCardId() {
  byte readCard[4];
  for (int i = 0; i < 4; i++) {
    readCard[i] = mfrc522.uid.uidByte[i];
  }

  return (unsigned long)readCard[0] << 24
    | (unsigned long)readCard[1] << 16
    | (unsigned long)readCard[2] << 8
    | (unsigned long)readCard[3];
}

ESPHOME Config same as above

esphome:
  name: rfidtag
  friendly_name: rfidtag

esp8266:
  board: d1_mini

mqtt:
  broker: 192.168.1.1

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxx="

ota:
  password: "xxxxxxxxxxxxxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Rfidtag Fallback Hotspot"
    password: "xxxxxxxxxxx"

captive_portal:
    
spi:
  clk_pin: D5
  miso_pin: D6
  mosi_pin: D7
rc522_spi:
  cs_pin: D8
  update_interval: 1s
  on_tag:
    then:
      - mqtt.publish:
          topic: spotify/rfid/id
          payload: !lambda 'return x;'
  on_tag_removed:
    then:
      - mqtt.publish:
          topic: spotify/rfid/idremoved
          payload: !lambda 'return x;'

Home Assistant and Harmony Hub Scripts

I’ve got a Logitech Harmony Hub to IR control devices.

When adding this to your home assistant, you initially only get the Activities.

But I want to add single button presses.
I was going to re-visit my IR remote project.
(The IR blaster and the IR wemos, see previous posts)

Thanks to Duncan I had a second look at this solution

Check the files using the file editor.
Search for /homeassistant/harmony_xxxxxxxx.conf

It will show the Hub devices and their capabilities.

Short example below

{
    "Activities": {
        "-1": "PowerOff",
        "42652474": "Watch TV",
        "43054933": "Squeeze",
        "43054959": "Watch Netflix",
        "43072690": "Switch",
        "43073688": "Spotify",
        "43557114": "Kodi",
        "49494467": "Tv on",
        "52476284": "Boxon",
        "52476285": "Boxoff"
    },
    "Devices": {
        "Google Chromecast": {
            "commands": [],
            "id": "67134460"
        },
        "LG TV": {
            "commands": [
                "PowerOff",
                "PowerOn",
                "PowerToggle",
                ".",
                "-",
                "0",
 -----------8<----------------------------- SNIP SNAP
               "SportsMode"
            ],
            "id": "67134658"
        },
        "Onkyo AV Receiver": {
            "commands": [
                "PowerOff",
                "PowerOn",
 -----------8<----------------------------- SNIP SNAP
                "VolumeLevelUp5Step"
            ],
            "id": "67134459"
        },
        "arcadyan DVR": {
            "commands": [
                "PowerToggle",
 -----------8<----------------------------- SNIP SNAP
                "TV"
            ],
            "id": "67134775"
        }
    }
}

Lets create a Script for this

See the IDs above to refer to commands below.

alias: Netflix
sequence:
  - service: remote.send_command
    target:
      device_id: 7e82a825decabedbc98a0b5ce2ac5d78
    data:
      num_repeats: 1
      delay_secs: 0.4
      hold_secs: 1.2
      command: PowerOn
      device: "67134658"
  - service: remote.send_command
    metadata: {}
    data:
      num_repeats: 1
      delay_secs: 0.4
      hold_secs: 0
      command: InputStb/Dvr
      device: "67134459"
    target:
      device_id: 7e82a825decabedbc98a0b5ce2ac5d78
  - service: remote.send_command
    metadata: {}
    data:
      num_repeats: 1
      delay_secs: 0.4
      hold_secs: 0
      command: Netflix
      device: "67134658"
    target:
      device_id: 7e82a825decabedbc98a0b5ce2ac5d78
mode: single

Add these scripts to buttons

type: entities
entities:
  - script.allmediaoff
  - script.netflix
  - script.tvon

Kodi camera stream push playlist

Below is a solution when you want to stream IP camera’s in Kodi/Libreelec .

You can push these commands using Nodered, Bash script or whatever.

First make some camera scripts in your profile directory.

Examples:

# Kodi on Linux/Raspberry
# Place a file cam1.m3u in .kodi/userdata/profiles/(kodiprofile)/playlists/video/
rtsp://admin:secretpass@192.168.1.123:88/videoMain

#and another one in cam2.m3u (another example mjpeg example)
http://192.168.1.124:8000/stream.mjpg

#For windows it is in
C:\Users\(Username)\AppData\Roaming\Kodi\userdata\profiles\(kodiprofile)\playlists\video

Enable http access in Kodi and run the playlist using curl

curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc

A bash loop script

while true; do
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam1.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
sleep 10
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
sleep 5
done