Skip to content

Instantly share code, notes, and snippets.

@cmsj
Last active December 31, 2023 01:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cmsj/a008d3bf044ad211605fdf9410f20791 to your computer and use it in GitHub Desktop.
Save cmsj/a008d3bf044ad211605fdf9410f20791 to your computer and use it in GitHub Desktop.
Home Assistant python_scripts script to render info from the Waste Collection Schedule HACS integration
# eInk Waste Collection Renderer
# v1.0 Copyright Chris Jones <cmsj@tenshu.net>
# Licensed under the MIT License
# This can be called as a Home Assistant service if you follow: https://www.home-assistant.io/integrations/python_script/
# Calendar is expected to be populated by the Waste Collection Schedule integration from HACS
# (although I only tested with my home region, it's likely the data is formatted differently for other regions)
# eInk tag to render to
tag_entity = "open_epaper_link.000002838f103b16"
# eInk tag battery entity to display
tag_battery_entity = "sensor.tag5_battery"
# Calendar entry to take information from
# This calendar can be auto-populated if you live in a region supported by:
# https://github.com/mampfes/hacs_waste_collection_schedule
cal_entity = "calendar.the_royal_borough_of_kingston_council"
# Start tag rendering with an offset from the edges of the screen
y = 2
x = 2
# Sizes for the two types of lines of text we'll render
day_size = 21
bin_size = 19
# Number of days to show data for
# Default is 13 because our garbage collections happen weekly and I want to show no more than two weeks
display_days = 13
# Define some metadata for different collection types so we can choose their names and icons
bin_types = {
"BLUE": {
"name": "Blue (cardboard)",
"icon": "package-variant"
},
"GREEN": {
"name": "Green(glass/metal/plastic)",
"icon": "glass-fragile"
},
"BLACK": {
"name": "Black (general)",
"icon": "delete-outline"
}
}
# Data storage we'll need later
tag_items = []
bin_days = {}
# Calculate the start and end dates for our calendar search
start_date_time = dt_util.start_of_local_day().replace(hour=0, minute=0, second=0)
end_date_time = (start_date_time.replace(hour=23, minute=59, second=59) + datetime.timedelta(days=display_days))
# Do the calendar search
cal_data = hass.services.call("calendar", "get_events", target={"entity_id": cal_entity},
service_data={"start_date_time": start_date_time, "end_date_time": end_date_time},
blocking=True, return_response=True)
# Loop over the calendar we searched, if it is in the results
if cal_entity not in cal_data:
# No calendar data was available, so render an error
tag_items.append({
"type": "text",
"value": "Unable to read calendar",
"x": x,
"y": y,
"size": 20,
"color": "red"
})
else:
logger.debug(cal_data)
# Loop over events in the current calendar
for event in cal_data[cal_entity]["events"]:
day = event["start"]
bin_type = event["summary"]
if day not in bin_days:
# This is a day we haven't seen yet, create an empty array to hold its items
bin_days[day] = []
# Process the collection types from the calendar. These likely need to change for different regions than ours
if bin_type == "food waste bin":
# We don't do this bin in our house
continue
elif bin_type == "mixed recycling bin":
bin_days[day].append("GREEN")
elif bin_type == "paper and card recycling bin":
bin_days[day].append("BLUE")
elif bin_type == "refuse bin":
bin_days[day].append("BLACK")
else:
logger.warning("Unknown bin type: %s" % bin_type)
bin_days[day].append(bin_type)
# Now we have all of the collection days neatly collected, loop over that data for rendering
for day in sorted(bin_days):
logger.info("Adding tag items for day: %s" % day)
# Calendar info just has a string for the day of the bin collection.
# Turn that into a human-readable delta, by way of a lot of annoying python date/time calls
collection_date_datetime = dt_util.as_local(datetime.datetime.fromtimestamp(time.mktime(time.strptime(day, "%Y-%m-%d"))))
collection_day_delta = collection_date_datetime - start_date_time
num_days = collection_day_delta.days
day_string = "Unknown"
if num_days == 0:
day_string = "Today"
elif num_days == 1:
day_string = "Tomorrow"
elif num_days > 6:
day_string = day
elif num_days > 1:
day_string = "In %s days" % num_days
# Render the date
tag_items.append({
"type": "text",
"value": day_string,
"x": 2,
"y": y,
"size": day_size,
"color": "black"
})
y = y + day_size
# Render the collections for that date
for bin in bin_days[day]:
# First the icon for this bin
tag_items.append({
"type": "icon",
"value": bin_types[bin]["icon"],
"x": 2,
"y": y + 1,
"size": bin_size,
"color": "red"
})
# Then the name
tag_items.append({
"type": "text",
"value": bin_types[bin]["name"],
"x": bin_size + 2,
"y": y,
"size": bin_size,
"color": "black"
})
y = y + bin_size + 4
y = y + 4
# Add "Last Modified" and battery info footer
cur_datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
battery_level = 0
try:
battery_level = int(hass.states.get(tag_battery_entity).state)
except ValueError:
pass
tag_items.append({
"type": "text",
"value": "Last updated: %s" % cur_datetime,
"size": 10,
"x": 45,
"y": 118,
"color": "black"
})
# Battery shape
tag_items.append({
"type": "rectangle",
"x_start": 275,
"y_start": 118,
"x_end": 295,
"y_end": 126,
"width": 1,
"fill": "white",
"outline": "black"
})
# Battery bar
tag_items.append({
"type": "rectangle",
"x_start": 293 - (battery_level / 100 * 16),
"y_start": 120,
"x_end": 293,
"y_end": 124,
"width": 1,
"fill": "black",
"outline": "black"
})
# Battery terminal
tag_items.append({
"type": "rectangle",
"x_start": 272,
"y_start": 120,
"x_end": 275,
"y_end": 124,
"width": 1,
"fill": "white",
"outline": "black"
})
# Dispatch our rendering data structure to OpenEPaperLink
hass.services.call("open_epaper_link", "drawcustom", target={"entity_id": [tag_entity]}, service_data={"background": "white", "ttl": 300, "payload": tag_items})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment