michaelharley

u/michaelharley@infosec.pub
12 posts · 12 comments

Recent posts

Recent comments

No. I do not use Google devices to call HA scripts/scenes. I play TTS announcements over my local network to the nest minis. The devices have physical switches on them to disable the mics and I have turned them all off.

Happy to! I'll do Lily, since it's the same block copy pasted five times, once per goblin.

Fair warning: I'm not a developer and there's almost certainly a tidier way to do all of this. It works though, and good enough is good enough.

The helpers

All of this is built on helpers, the ones under Settings > Devices & services > Helpers. Birthday and gotcha date are each a date/time helper set to date only, no time. I keep mine in input_datetime.yaml, but making them in the UI gets you the exact same thing:

lily_birthday:
  name: Lily Birthday
  icon: mdi:cake-variant
  has_date: true
  has_time: false
lily_gotcha_date:
  name: Lily Gotcha Date
  icon: mdi:home-heart
  has_date: true
  has_time: false

The collar needs three more: one date/time helper for the expiration date, plus two toggle helpers that my alert automation flips on and off. I made these ones in the UI so they live in .storage instead of a file, but here's the shape:

# input_datetime
lily_flea_collar_expiration:
  name: Lily Flea Collar Expiration
  has_date: true
  has_time: false

# input_boolean
lily_flea_collar_expired:
  name: Lily Flea Collar Expired
dismiss_lily_flea_collar_alert:
  name: Dismiss Lily Flea Collar Alert

I left initial: off all of them on purpose. If you set it, the next restart resets the helper back to that default and throws away whatever date you actually put in. Took me a restart or two to work that one out.

Age and With Us

These two are Mushroom template cards on the dashboard (Mushroom is a HACS install if you don't already have it). Add a manual card, hit "Show code editor" and paste. It's the same card twice, just pointed at the other helper:

type: custom:mushroom-template-card
primary: Age
entity: input_datetime.lily_birthday
icon: mdi:cake-variant
icon_color: blue
secondary: >-
  {% set b = state_attr('input_datetime.lily_birthday','timestamp') %}
  {% set by = b | timestamp_custom('%Y', true) | int %}
  {% set bm = b | timestamp_custom('%m', true) | int %}
  {% set bd = b | timestamp_custom('%d', true) | int %}
  {% set months = (now().year - by) * 12 + (now().month - bm)
     - (1 if now().day < bd else 0) %}
  {% set y = (months // 12) | int %}
  {% set mo = (months % 12) | int %}
  {%- if y > 0 %}{{ y }} yr{{ 's' if y != 1 }}{% endif %}
  {%- if mo > 0 %}{{ ' ' if y > 0 }}{{ mo }} mo{% endif %}
  {%- if y == 0 and mo == 0 %}new{% endif %}
  · {{ b | timestamp_custom('%b %-d, %Y', true) }}
tap_action:
  action: more-info

Mine reads 7 yrs 3 mo · Apr 2, 2023.

One thing worth calling out: counting whole months first and then dividing by 12 avoids the leap year drift you get from dividing days by 365.

The collar card

Another template card, this one reading the expiration helper. It's what goes green, orange, red as the days run down:

type: custom:mushroom-template-card
primary: Flea Collar
entity: input_datetime.lily_flea_collar_expiration
icon: >-
  {% set ts = state_attr(entity,'timestamp') %}
  {% set days = ((ts - as_timestamp(today_at('00:00'))) / 86400) | round(0) | int %}
  {% if days < 0 %}mdi:alert{% elif days <= 30 %}mdi:alert-outline
  {% else %}mdi:shield-check{% endif %}
icon_color: >-
  {% set ts = state_attr(entity,'timestamp') %}
  {% set days = ((ts - as_timestamp(today_at('00:00'))) / 86400) | round(0) | int %}
  {% if days < 0 %}red{% elif days <= 30 %}orange{% else %}green{% endif %}
secondary: >-
  {% set ts = state_attr(entity,'timestamp') %}
  {% set days = ((ts - as_timestamp(today_at('00:00'))) / 86400) | round(0) | int %}
  {% if days < 0 %}Expired {{ days * -1 }}d ago{% else %}{{ days }}d left{% endif %}
  · {{ ts | timestamp_custom('%b %-d, %Y', true) }}
tap_action:
  action: more-info

I compare against today_at('00:00') instead of now(). With now() the count flips over at whatever time of day I happened to put the collar on, so it'd read a day short all afternoon. Little thing, but it bugged me.

The reset script

This part isn't a card, it's a script. Settings > Automations & scenes > Scripts, create one, then use the three dot menu to edit it in YAML. Mine lives in scripts.yaml:

reset_lily_flea_collar:
  alias: Reset Lily Flea Collar
  description: Sets Lily's flea collar expiration to today + 7 months and clears alerts
  icon: mdi:cat
  sequence:
    - action: input_datetime.set_datetime
      target:
        entity_id: input_datetime.lily_flea_collar_expiration
      data:
        date: "{{ (now() + timedelta(days=213)).strftime('%Y-%m-%d') }}"
    - action: input_boolean.turn_off
      target:
        entity_id: input_boolean.lily_flea_collar_expired
    - action: input_boolean.turn_off
      target:
        entity_id: input_boolean.dismiss_lily_flea_collar_alert

And the button that fires it, which is one more template card back on the dashboard:

type: custom:mushroom-template-card
primary: Reset Collar
secondary: Expiry 7 months out
entity: script.reset_lily_flea_collar
icon: mdi:restart
tap_action:
  action: perform-action
  perform_action: script.reset_lily_flea_collar
  confirmation:
    text: Reset Lily's flea collar expiration to 7 months from today?

The confirmation is in there because I tap this on my phone and I have fat fingers.

Those two input_booleans are for my alert automation. _expired flips on when the date passes so it gets read out in our good morning announcement, and dismiss_ shuts it up once I've actually heard it (otherwise it just keeps announcing it, every single morning, forever). If you're not doing alerts you can drop those steps and the helpers along with them.

Hope that helps! Let me know if something doesn't work, I probably typed something wrong. Are you tracking vaccines too? That's the one I still need to build.

Thanks! Both are the same trick: an input_datetime per cat, and a Mushroom template card doing the math inline in Jinja. No custom integration.

The collar card colors off days remaining, and the reset button runs a script that pushes the date out and clears the alert flags.

Want the YAML?

My favorite thing to self host is my RSS feed reader. I'm a huge fan of feedbin but I wanted to self host. I tried the usual self hosted RSS feed readers but I just prefer the way feedbin is laid out and how it works. So, I used Claude to create a self hosted clone of feedbin but with my personal modification to fit my workflow.

  • Customized keyboard shortcuts for next and previous.
  • I added webmention support which allows me:
    • Determine if a site supports webmentions
    • Allows me to send a webmention like
    • Allows me to send a webmention comment
  • I can share and sydicate a link from my feed reader to my website
  • I can save a link as a blog idea, like if I want to respond or write about a topic for example.
  • Custom filters so I can basically mute certain keywords from me feed.

Short version, and Deebster's got the shape right: use a DNS-01 challenge. You prove domain control with a TXT record instead of serving a file over HTTP, so the hostname never has to be reachable from the internet. That's what gets you a real Let's Encrypt cert for a LAN-only name.

Mine's acme.sh issuing a wildcard for *.lan.michaelharley.net via my registrar's DNS API (Porkbun), with a deploy hook dropping it where Caddy reads it. Caddy's own Cloudflare plugin is simpler, I just already had acme.sh. Local DNS is a wildcard rewrite on my router.

Sorry about the managed post at first.

I'm new to the community so sorry about the basic question but is this type of post ok here? This is a blog post that I put on my site. Do we prefer to have the full post like this with the link or do we prefer to just have a link and intro?

I run Calibre-Web in Docker (linuxserver.io image) and read on a Kobo. My desktop Calibre library on my laptop is the source of truth; Syncthing replicates it to the server where Calibre-Web serves it, and the Kobo pulls books over Kobo Sync