Tagreader jukebox: old dog, new tricks!

i cannot get the blueprint device selection to work.
It seems it is looking for a manufacturer ‘espressif’, while the tagreader has the manufacturing field as ‘by adonno’
How to fix?

Try this until luka6000 changes the original:

import the link as blueprint

EDIT: After playing with it one question:

Ist possible to use URI instead of URLs?
Like spotify:album:EGEGEFEFE
instead of https://open.spotify.com/album etc…?

This would open the spotify app on my phone when scanning the nfc chip, instead of opening the browser.

Thanks for your script!

I’ve deleted manufacturer selector so now it should work with tag readers by adonno and any other.

1 Like

how does scanning of URL works on Android? On iOS when I scan URL of the music service, usually the app has registered it’s urls so the browser instantly switches to the app. That’s the whole point of this URL tag - it will work on every setup so you can give the tag card to a friend and it will work with his/her phone and tag reader

have you tried? I don’t think it will work right now. To do this you should introduce another tag type source and a variable to be populated as a template from tag data. It sounds a bit complicated but I think it is possible.

1 Like

yeah, its not the same with android (at least with my motorola defy)…
It opens the default browser with the spotify album/playlist and there is a button to open spotify app

Thats true, seems i need to change the script for that.

Yes, it just does not work with URI

I Love all of what I see so far, only issue I am having is that my HA doesn’t seem to see any of the esphome.music_tag events. When I watch for tag_scanned, I see those, but not when I watch for music_tag (or esphome.music_tag). Any idea what is wrong with my setup? I’ve also confirmed the music url on the tag works for launching the music directly on my android phone.

Can you send tagreader console output? I think you’re not using tag writer service correctly.

1 Like

Yes, here is what I am getting:

[09:16:06][D][pn532:280]: Mifare classic
[09:16:07][D][light:035]: 'TagReader LED' Setting:
[09:16:07][D][light:046]:   State: ON
[09:16:07][D][light:050]:   Brightness: 100%
[09:16:07][D][light:057]:   Red: 0%, Green: 100%, Blue: 0%
[09:16:07][D][light:077]:   Flash length: 0.5s
[09:16:07][D][pn532:162]: Found new tag '8A-7D-7D-82'
[09:16:07][D][pn532:166]:   NDEF formatted records:
[09:16:07][D][pn532:168]:     U - http://listen.plex.tv/player/playback/playMedia?uri=server[...]
[09:16:07][D][pn532:168]:     T - Testing
[09:16:07][D][pn532:295]: Waiting to read next tag
[09:16:07][D][tagreader:292]: Found music info tag NDEF
[09:16:07][D][tagreader:309]: No HA NDEF, using UID
[09:16:07][D][rtttl:038]: Playing song success
[09:16:08][D][rtttl:094]: Playback finished

where “[…]” is the rest of the url that works when read with my phone’s NFC tag.

I know this isn’t going to work with the standard blueprint, so I’ve already started creating my own blueprint and system to have home assistant trigger playback another way, but what I realized is that my automation based on my new blueprint doesn’t triggered if I listen for esphome.music_tag and doesn’t have the data (of course) when I listen for tag_scanned. That is when I started to listen directly with developer tools event listening to see if it was an automation problem or an event problem, or something else.

Also, if it can help, this is the config I used to call the tag_writer service (from the dev tools)

service: esphome.tagreader_dc64ac_write_music_tag
data:
  music_url: >-
    http://listen.plex.tv/player/playback/playMedia?uri=server[...]
  music_info: Testing

ok, it’s clear now. This is not working with any URL. Currently firmware can recognize only these services

            size_t hass = payload.find("https://www.home-assistant.io/tag/");
            size_t applemusic = payload.find("https://music.apple.com");
            size_t spotify = payload.find("https://open.spotify.com");
            size_t sonos = payload.find("sonos-2://");

and if URL is not recognized, it falls back to hass tag. That’s why you see tag_scanned events.
I think it could be done completely different meaning any URL on the card could be pushed as dedicated event and all url parsing could be done in automation. I don’t have much time now to do this. Do you think it’s worth it? I must say after all this time I see limitations of my previous work. On the other hand it is purpose build (Spotify, Apple Music, Sonos) and works seamlessly.

Ahhh, that makes sense! I do think it would be worth adding a custom option, that can be used by people, like me, who want to do more.

If I wanted to do it, (I used to be a programmer), would it be as simple as or branching/forking tagreader/tagreader.yaml at 7bc9ec90bf8976eb9e5b85a720ef046a64ab5d03 · adonno/tagreader · GitHub and telling my reader to download from my fork/branch?

try using this one

If you can test it, I can make pull request to Adonno. This yaml was already on the verge of 8266 memory capabilities so we need to be careful with new code

1 Like

First off, thank you so much for your help with this and fast response times! I am stoked to get this working!

Is this how I would install your branch?

substitutions:
  name: tagreader-dc64ac
packages:
  adonno.tag_reader: github://luka6000/tagreader/blob/luka6000-patch-anyurl/tagreader.yaml
esphome:
  name: ${name}
  name_add_mac_suffix: false


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

If so I am running into an issue when I click install:

INFO Reading configuration /config/esphome/tagreader-dc64ac.yaml...
ERROR Unexpected exception while reading configuration:
Traceback (most recent call last):
  File "/usr/local/bin/esphome", line 33, in <module>
    sys.exit(load_entry_point('esphome', 'console_scripts', 'esphome')())
  File "/esphome/esphome/__main__.py", line 960, in main
    return run_esphome(sys.argv)
  File "/esphome/esphome/__main__.py", line 938, in run_esphome
    config = read_config(dict(args.substitution) if args.substitution else {})
  File "/esphome/esphome/config.py", line 977, in read_config
    res = load_config(command_line_substitutions)
  File "/esphome/esphome/config.py", line 831, in load_config
    return _load_config(command_line_substitutions)
  File "/esphome/esphome/config.py", line 819, in _load_config
    result = validate_config(config, command_line_substitutions)
  File "/esphome/esphome/config.py", line 681, in validate_config
    substitutions.do_substitution_pass(config, command_line_substitutions)
  File "/esphome/esphome/components/substitutions/__init__.py", line 154, in do_substitution_pass
    config.move_to_end(CONF_SUBSTITUTIONS, False)
AttributeError: 'dict' object has no attribute 'move_to_end'

Apologies if this is a noob mistake. I am extremely new to ESPhome devices.

I am using the ESPHome addon in HomeAssistant, fyi.

sorry, my mistake. That’s what happens when you try to change code you haven seen for a long time.
Try again - this compiled for my tag reader now.
If you will still have errors, try using your working configuration and add this changes

I think I am doing something wrong with the package section, I’ve tried different things, and I can get this to work, but can’t figure out how to specify with the branch in a way that works:

packages:
  adonno.tag_reader: github://luka6000/tagreader/tagreader.yaml 

I am honestly not sure how to edit my working config…

Just copy-paste whole yaml and comment out this package statement

1 Like

If you saw my previous version(s) of this message, I got past where I was stuck. Will post more updates as I learn/test.

It works!

This is exactly what I finally got to flash and successfully tested minus my specific IP addresses (for you to use with beyond compare or similar, to make sure I didn’t make other changes I forgot about to get it working)

# Insert your SSID and Your PWD after initial setup
wifi:
  ssid: [redacted]
  password: [redacted]
  manual_ip:
    static_ip: [redacted]
    gateway: [redacted]
    subnet: [redacted]

# Enable the captive portal for initial WiFi setup
captive_portal:

improv_serial:

substitutions:
  name: tagreader-dc64ac
  friendly_name: TagReader

# packages:
#   adonno.tag_reader: github://adonno/tagreader/tagreader.yaml

esphome:
  name: $name
  platform: ESP8266
  board: d1_mini

  # Automatically add the mac address to the name
  # so you can use a single firmware for all devices
  name_add_mac_suffix: true

  # This will allow for (future) project identification,
  # configuration and updates.
  # project:
  #   name: adonno.tag_reader
  #   version: "1.4"
# If buzzer is enabled, notify on api connection success
  on_boot:
    priority: -10
    then:
    - wait_until:
        api.connected:
    - logger.log: API is connected!
    - rtttl.play: "success:d=24,o=5,b=100:c,g,b"
    - light.turn_on:
        id: activity_led
        brightness: 100%
        red: 0%
        green: 0%
        blue: 100%
        flash_length: 500ms
    - switch.turn_on: buzzer_enabled
    - switch.turn_on: led_enabled

# Define switches to control LED and buzzer from HA
switch:
- platform: template
  name: "${friendly_name} Buzzer Enabled"
  id: buzzer_enabled
  icon: mdi:volume-high
  optimistic: true
  restore_state: true
  entity_category: config
- platform: template
  name: "${friendly_name} LED enabled"
  id: led_enabled
  icon: mdi:alarm-light-outline
  optimistic: true
  restore_state: true
  entity_category: config
  
# Define buttons for writing tags via HA 
button:
  - platform: template
    name: Write Tag Random
    id: write_tag_random
    # Optional variables:
    icon: "mdi:pencil-box"
    on_press:
      then:
      - light.turn_on:
          id: activity_led
          brightness: 100%
          red: 100%
          green: 0%
          blue: 100%    
      - lambda: |-
          static const char alphanum[] = "0123456789abcdef";
          std::string uri = "https://www.home-assistant.io/tag/";
          for (int i = 0; i < 8; i++)
            uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
          uri += "-";
          for (int j = 0; j < 3; j++) {
            for (int i = 0; i < 4; i++)
              uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
            uri += "-";
          }
          for (int i = 0; i < 12; i++)
            uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
          auto message = new nfc::NdefMessage();
          message->add_uri_record(uri);
          ESP_LOGD("tagreader", "Writing payload: %s", uri.c_str());
          id(pn532_board).write_mode(message);
      - rtttl.play: "write:d=24,o=5,b=100:b"
      - wait_until:
          not:
            pn532.is_writing:
      - light.turn_off:
          id: activity_led
      - rtttl.play: "write:d=24,o=5,b=100:b,b"
  - platform: template
    name: Clean Tag
    id: clean_tag
    icon: "mdi:nfc-variant-off"
    on_press:
      then:
      - light.turn_on:
          id: activity_led
          brightness: 100%
          red: 100%
          green: 64.7%
          blue: 0%    
      - lambda: 'id(pn532_board).clean_mode();'
      - rtttl.play: "write:d=24,o=5,b=100:b"
      - wait_until:
          not:
            pn532.is_writing:
      - light.turn_off:
          id: activity_led
      - rtttl.play: "write:d=24,o=5,b=100:b,b"
  - platform: template
    name: Cancel writing 
    id: cancel_writing
    icon: "mdi:pencil-off"
    on_press:
      then:
      - lambda: 'id(pn532_board).read_mode();'
      - light.turn_off:
          id: activity_led
      - rtttl.play: "write:d=24,o=5,b=100:b,b"

  - platform: restart
    name: "${friendly_name} Restart"
    entity_category: config
# Enable logging
logger:
  # level: VERY_VERBOSE
  # level: VERBOSE

# Enable Home Assistant API
api:
  services:
  - service: rfidreader_tag_ok
    then:
    - rtttl.play: "beep:d=16,o=5,b=100:b"

  - service: rfidreader_tag_ko
    then:
    - rtttl.play: "beep:d=8,o=5,b=100:b"

  - service: play_rtttl
    variables:
      song_str: string
    then:
    - rtttl.play: !lambda 'return song_str;'

  - service: write_tag_id
    variables:
      tag_id: string
    then:
    - light.turn_on:
        id: activity_led
        brightness: 100%
        red: 100%
        green: 0%
        blue: 0%    
    - lambda: |-
        auto message = new nfc::NdefMessage();
        std::string uri = "https://www.home-assistant.io/tag/";
        uri += tag_id;
        message->add_uri_record(uri);
        id(pn532_board).write_mode(message);
    - rtttl.play: "write:d=24,o=5,b=100:b"
    - wait_until:
        not:
          pn532.is_writing:
    - light.turn_off:
        id: activity_led
    - rtttl.play: "write:d=24,o=5,b=100:b,b"

  - service: write_music_tag
    variables:
      music_url: string
      music_info: string
    then:
    - light.turn_on:
        id: activity_led
        brightness: 100%
        red: 100%
        green: 0%
        blue: 0%    
    - lambda: |-
        auto message = new nfc::NdefMessage();
        std::string uri = "";
        std::string text = "";
        uri += music_url;
        text += music_info;
        if ( music_url != "" ) {
          message->add_uri_record(uri);
        }
        if ( music_info != "" ) {
          message->add_text_record(text);
        }
        id(pn532_board).write_mode(message);
    - rtttl.play: "write:d=24,o=5,b=100:b"
    - wait_until:
        not:
          pn532.is_writing:
    - light.turn_off:
        id: activity_led
    - rtttl.play: "write:d=24,o=5,b=100:b,b"

# Enable OTA upgrade
ota:

i2c:
  scan: False
  frequency: 400kHz

globals:
  - id: source
    type: std::string
  - id: url
    type: std::string
  - id: info
    type: std::string

pn532_i2c:
  id: pn532_board
  on_tag:
    then:
    - if:
        condition:
          switch.is_on: led_enabled
        then:
        - light.turn_on:
            id: activity_led
            brightness: 100%
            red: 0%
            green: 100%
            blue: 0%
            flash_length: 500ms
    
    - delay: 0.15s #to fix slow component
        
    - lambda: |-
        id(source)="";
        id(url)="";
        id(info)="";
        if (tag.has_ndef_message()) {
          auto message = tag.get_ndef_message();
          auto records = message->get_records();
          for (auto &record : records) {
            std::string payload = record->get_payload();
            std::string type = record->get_type();
            size_t hass = payload.find("https://www.home-assistant.io/tag/");
            size_t applemusic = payload.find("https://music.apple.com");
            size_t spotify = payload.find("https://open.spotify.com");
            size_t uri = payload.find("https://");
            size_t sonos = payload.find("sonos-2://");

            if (type == "U" and hass != std::string::npos ) {
              ESP_LOGD("tagreader", "Found Home Assistant tag NDEF");
              id(source)="hass";
              id(url)=payload;
              id(info)=payload.substr(hass + 34);
            }
            else if (type == "U" and applemusic != std::string::npos ) {
              ESP_LOGD("tagreader", "Found Apple Music tag NDEF");
              id(source)="amusic";
              id(url)=payload;
            }
            else if (type == "U" and spotify != std::string::npos ) {
              ESP_LOGD("tagreader", "Found Spotify tag NDEF");
              id(source)="spotify";
              id(url)=payload;
            }
            else if (type == "U" and sonos != std::string::npos ) {
              ESP_LOGD("tagreader", "Found Sonos app tag NDEF");
              id(source)="sonos";
              id(url)=payload;
            }
            else if (type == "U" and uri != std::string::npos ) {
              ESP_LOGD("tagreader", "Found other URL tag NDEF");
              id(source)="uri";
              id(url)=payload;
            }
            else if (type == "T" ) {
              ESP_LOGD("tagreader", "Found music info tag NDEF");
              id(info)=payload;
            }
            else if ( id(source)=="" ) {
              id(source)="uid";
            }
          }
        }
        else {
          id(source)="uid";
        }
    
    - if:
        condition:
          lambda: 'return ( id(source)=="uid" );'
        then:
          - homeassistant.tag_scanned: !lambda |-
              ESP_LOGD("tagreader", "No HA NDEF, using UID");
              return x;
        else:
        - if:
            condition:
              lambda: 'return ( id(source)=="hass" );'
            then:
            - homeassistant.tag_scanned: !lambda 'return id(info);'
            else:
            - homeassistant.event:
                event: esphome.music_tag
                data:
                  reader: !lambda |-
                    return App.get_name().c_str();
                  source: !lambda |-
                    return id(source);
                  url: !lambda |-
                    return id(url);
                  info: !lambda |-
                    return id(info);
    
    - if:
        condition:
          switch.is_on: buzzer_enabled
        then:
        - rtttl.play: "success:d=24,o=5,b=100:c,g,b"
  on_tag_removed:
    then:
    - homeassistant.event:
        event: esphome.tag_removed
# Define the buzzer output
output:
- platform: esp8266_pwm
  pin: D7
  id: buzzer

binary_sensor:
  - platform: status
    name: "${friendly_name} Status"
    entity_category: diagnostic


text_sensor:
  - platform: version
    hide_timestamp: true
    name: "${friendly_name} ESPHome Version"
    entity_category: diagnostic
  - platform: wifi_info
    ip_address:
      name: "${friendly_name} IP Address"
      icon: mdi:wifi
      entity_category: diagnostic
    ssid:
      name: "${friendly_name} Connected SSID"
      icon: mdi:wifi-strength-2
      entity_category: diagnostic

# Define buzzer as output for RTTTL
rtttl:
  output: buzzer

# Configure LED
light:
- platform: neopixelbus
  variant: WS2812
  pin: D8
  num_leds: 1
  flash_transition_length: 500ms
  type: GRB
  id: activity_led
  name: "${friendly_name} LED"
  restore_mode: ALWAYS_OFF

I’ll get a litle further with the HA side of parsing and using and if you are interested, I can share it as an example. Currently, it involves HA and Tasker and AutoRemote and Plex and AirMusic :smiley: There are probably a lot of simpler use cases for this new option.

Looking forward to the patch getting applied and upgrading 8 of my tag readers :smiley:

Thank you so much again for being so responsive and awesome to work with. At some point, I’ll try to look at the code and try to think if there is a way to trim it down to help out… (and have some fun).

2 Likes

Finished testing and shared back what exactly I used. All looks good! Would you mind letting me know when you are able to submit the pull request so that I can apply the updates as soon as they are available? Let me know if I can help!