Skip to content

Dashboard-blocks-example#174

Open
tyeth wants to merge 2 commits intomasterfrom
dashboard-blocks-example
Open

Dashboard-blocks-example#174
tyeth wants to merge 2 commits intomasterfrom
dashboard-blocks-example

Conversation

@tyeth
Copy link
Member

@tyeth tyeth commented Mar 6, 2026

@tyeth review this with fresh eyes. It's overly long, but effective, maybe a better comment header or something.

@tyeth tyeth self-assigned this Mar 6, 2026
@tyeth tyeth requested a review from Copilot March 6, 2026 19:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR rewrites the examples/basics/dashboard.py example to demonstrate creating a dashboard with every supported Adafruit IO block type via the REST API (previously only 3 block types were shown). It also makes minor documentation improvements to two weather example files.

Changes:

  • examples/basics/dashboard.py: Complete rewrite — feeds are bootstrapped, account limits are checked, one block of each visual type is created, and a responsive layout is applied.
  • examples/api/weather.py: Docstring updated from "Dark Sky Hyperlocal" to "Apple WeatherKit example" and an inline comment added to the receive_weather call.
  • examples/api/weather_create_delete.py: Docstring updated to note "IO Plus" requirement.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
examples/basics/dashboard.py Full rewrite: feeds/data bootstrapping, limit checking, all-block-type creation, responsive layout
examples/api/weather.py Docstring and inline comment update to reflect current weather provider
examples/api/weather_create_delete.py Docstring updated to add "(IO Plus)" clarification

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +401 to +405
if visual_type in ("line_chart", "stream", "map"):
feed_id = secondary_feed_id

group_id = primary_group_id
if visual_type in ("line_chart", "stream", "map"):
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In make_block, the conditions on lines 401 and 405 hardcode the set ("line_chart", "stream", "map") for choosing between the primary and secondary feed, duplicating the information already in the module-level constant SECONDARY_FEED_BLOCK_TYPES. For consistency and easier maintenance, replace both inline tuple checks with if visual_type in SECONDARY_FEED_BLOCK_TYPES.

Suggested change
if visual_type in ("line_chart", "stream", "map"):
feed_id = secondary_feed_id
group_id = primary_group_id
if visual_type in ("line_chart", "stream", "map"):
if visual_type in SECONDARY_FEED_BLOCK_TYPES:
feed_id = secondary_feed_id
group_id = primary_group_id
if visual_type in SECONDARY_FEED_BLOCK_TYPES:

Copilot uses AI. Check for mistakes.
continue
if not is_numeric_value(raw_feed.get("last_value")):
continue
usable.append(aio.feeds(feed_key))
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

select_usable_feeds calls aio.feeds(feed_key) individually for every qualifying feed (line 369), resulting in one extra API request per feed. Since the raw feed data from get_all_feeds_raw already contains all the needed fields (name, key, id), constructing a Feed object from the raw dict directly (e.g. Feed.from_dict(raw_feed)) would avoid these extra requests and improve performance, especially for accounts with many feeds.

Suggested change
usable.append(aio.feeds(feed_key))
usable.append(Feed.from_dict(raw_feed))

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +74
PUBLIC_DASHBOARD_USERNAME = os.getenv("PUBLIC_DASHBOARD_USERNAME", "tyeth")
PUBLIC_DASHBOARD_KEY = os.getenv("PUBLIC_DASHBOARD_KEY", "2025-06-24-sliders-and-toggle")
PUBLIC_DASHBOARD_NAME = os.getenv("PUBLIC_DASHBOARD_NAME", "2025-06-24 sliders and toggle")
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default values for PUBLIC_DASHBOARD_USERNAME, PUBLIC_DASHBOARD_KEY, and PUBLIC_DASHBOARD_NAME are hardcoded to a specific personal account ("tyeth") and a specific dashboard ("2025-06-24-sliders-and-toggle"). When this example is run without setting the corresponding environment variables, it will silently reference a specific private user's dashboard. For a published example, the default should be an empty string (or clearly indicate an invalid placeholder) so users know they must set these values themselves.

Suggested change
PUBLIC_DASHBOARD_USERNAME = os.getenv("PUBLIC_DASHBOARD_USERNAME", "tyeth")
PUBLIC_DASHBOARD_KEY = os.getenv("PUBLIC_DASHBOARD_KEY", "2025-06-24-sliders-and-toggle")
PUBLIC_DASHBOARD_NAME = os.getenv("PUBLIC_DASHBOARD_NAME", "2025-06-24 sliders and toggle")
PUBLIC_DASHBOARD_USERNAME = os.getenv("PUBLIC_DASHBOARD_USERNAME", "")
PUBLIC_DASHBOARD_KEY = os.getenv("PUBLIC_DASHBOARD_KEY", "")
PUBLIC_DASHBOARD_NAME = os.getenv("PUBLIC_DASHBOARD_NAME", "")

Copilot uses AI. Check for mistakes.
Comment on lines +477 to +487
elif visual_type == "selector":
properties = {
"label": "Selector",
"min": "0",
"max": "100",
"step": "50",
"onText": "High",
"offText": "Low",
"onValue": "100",
"offValue": "0",
}
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The selector visual type has a full properties definition here in make_block (lines 477–487), but "selector" is commented out of BLOCK_TYPES (line 61). This dead code branch for selector can never be reached and is confusing to readers. Either remove the elif visual_type == "selector" block or leave a short comment noting it is kept for reference alongside the commented-out BLOCK_TYPES entry.

Suggested change
elif visual_type == "selector":
properties = {
"label": "Selector",
"min": "0",
"max": "100",
"step": "50",
"onText": "High",
"offText": "Low",
"onValue": "100",
"offValue": "0",
}

Copilot uses AI. Check for mistakes.
Comment on lines +302 to +305
feed_limit, feed_remaining = extract_feed_limits(user_info)
group_limit, group_remaining = extract_group_limits(user_info)
dashboard_limit, dashboard_remaining = extract_dashboard_limits(user_info)

Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log_account_limits_and_usage is a diagnostics/logging-only function, but it calls extract_feed_limits and extract_dashboard_limits which each raise a RuntimeError if neither limit nor remaining value can be parsed from the API response. An unexpected API response format would therefore abort main() before any feeds or dashboard are created. Consider wrapping these calls in a try/except inside log_account_limits_and_usage, or changing extract_feed_limits/extract_dashboard_limits to return (None, None) gracefully (like extract_group_limits already does) rather than raising.

Suggested change
feed_limit, feed_remaining = extract_feed_limits(user_info)
group_limit, group_remaining = extract_group_limits(user_info)
dashboard_limit, dashboard_remaining = extract_dashboard_limits(user_info)
# Default to unknown limits; fall back to current counts if parsing fails.
feed_limit = None
feed_remaining = None
dashboard_limit = None
dashboard_remaining = None
try:
feed_limit, feed_remaining = extract_feed_limits(user_info)
except Exception as error:
print(" Feeds limits unavailable ({0})".format(error))
group_limit, group_remaining = extract_group_limits(user_info)
try:
dashboard_limit, dashboard_remaining = extract_dashboard_limits(user_info)
except Exception as error:
print(" Dashboards limits unavailable ({0})".format(error))

Copilot uses AI. Check for mistakes.
Comment on lines +768 to +775
if unbound_blocks:
raise RuntimeError(
"Feed association required for all widgets except divider, but these blocks "
"were created without linked feeds: {0}".format(
", ".join(unbound_blocks)
)
)

Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At lines 752–764, a warning message is printed telling the user to "Attach feed '{key}' manually if needed", but immediately afterwards (lines 768–774) a RuntimeError is raised that aborts the script. The two messages are contradictory: the warning implies the script will continue, but the exception terminates execution. Either remove the warning and only raise the exception, or remove the exception and let the script continue with the warning (if missing feed associations are considered acceptable).

Suggested change
if unbound_blocks:
raise RuntimeError(
"Feed association required for all widgets except divider, but these blocks "
"were created without linked feeds: {0}".format(
", ".join(unbound_blocks)
)
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants