Skip to content

Add YAML metadata support for genericmiot#2062

Merged
rytilahti merged 9 commits into
rytilahti:masterfrom
Acrobot:genericmiot-metadata
Jul 21, 2026
Merged

Add YAML metadata support for genericmiot#2062
rytilahti merged 9 commits into
rytilahti:masterfrom
Acrobot:genericmiot-metadata

Conversation

@Acrobot

@Acrobot Acrobot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Implements the metadata approach from #1618, adapted to the current codebase where descriptor creation has moved into the miot models.

Adds a YAML-based metadata system that enriches genericmiot descriptors with human-readable descriptions, replacing the vendor-provided, often-Chinese defaults from miotspec files:

  • Metadata is organized into namespace YAML files mirroring the MIoT URN namespace structure
  • A fallback chain allows dreame-specific services to fall back to miot-spec-v2, and all namespaces (including unknown ones) to fall back to common
  • A __ANY__ wildcard service in common provides cross-service property descriptions (temperature, humidity, CO2, PM2.5/PM10, voltage, etc.) without per-service repetition
  • Descriptor enrichment is non-mutating: the original descriptor is preserved in extras['original'] for callers that need the raw device-given name

Design

  • meta.py: Metadata class loads YAML namespace files and looks up metadata by URN namespace, service, type, and entity name
  • metadata/base.yaml: index mapping namespace names to YAML files
  • metadata/miotspec.yaml: definitions for miot-spec-v2 (battery, vacuum, filter, brush, light, identify services)
  • metadata/dreamespec.yaml: definitions for dreame-spec (vacuum-extend, DnD, audio, clean-logs, time services); falls back to miot-spec-v2
  • metadata/common.yaml: cross-namespace definitions under a __ANY__ wildcard service (MAC address, voltage, temperature, humidity, CO2, PM2.5/PM10, brightness, cleaning stats); implicit fallback for all namespaces including unknown ones
  • genericmiot.py: _enrich_with_metadata() returns a new descriptor via attr.evolve with the metadata name; the original descriptor is preserved in extras['original'] for callers that need the raw device-given name

Namespace fallback chain

dreame-spec → miot-spec-v2 → common
<any unknown namespace> → common

Namespaces declare their fallback with a fallback: key. common is the implicit fallback for all namespaces. Within each namespace, the __ANY__ wildcard service matches properties regardless of which service they belong to.

Model-specific namespaces (keyed by urn.model) are a natural future extension: add a model YAML file with a fallback: pointing to the relevant namespace.

@Acrobot

Acrobot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

These changes were generated using Claude Code under my supervision. I reviewed and approved all code before submitting. I'm not very familiar with this codebase, so apologies in advance for any mistakes.

@Acrobot
Acrobot marked this pull request as ready for review March 27, 2026 00:51
@Acrobot Acrobot mentioned this pull request Mar 27, 2026
22 tasks
@rytilahti rytilahti added this to the 0.6.0 milestone Jul 5, 2026
Adds a metadata system that enriches MIoT descriptors with
human-readable descriptions, icons, and device_class attributes
from YAML files, replacing the often-Chinese or generic defaults
from miotspec. Includes metadata for miot-spec-v2 and dreame-spec
namespaces.

Based on the approach from rytilahti#1618, adapted to the current codebase
where descriptor creation lives in the models.

Also adds MiotBaseModel.full_name and fixes a pre-existing ruff
E721 lint error in MiotProperty.pretty_value.
@rytilahti
rytilahti force-pushed the genericmiot-metadata branch from 358c5b4 to 1ec4957 Compare July 21, 2026 19:44
_enrich_with_metadata now returns a new descriptor via attr.evolve with
name set to the metadata description. The original descriptor is stored
in extras['original'] so callers can access the raw device-given name.
Move the type/name lookup into ServiceMeta.get() where it belongs,
reducing nesting in _lookup_in_namespace to a single walrus expression.
…full_name

Empty dict defaults eliminate all None guards in _lookup_in_namespace
and ServiceMeta.get(). Also removes MiotBaseModel.full_name which was
added on this branch but never used.
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.32143% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.27%. Comparing base (2817647) to head (01c3805).

Files with missing lines Patch % Lines
miio/integrations/genericmiot/meta.py 95.71% 2 Missing and 1 partial ⚠️
miio/integrations/genericmiot/genericmiot.py 85.71% 2 Missing ⚠️
miio/miot_models.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2062      +/-   ##
==========================================
+ Coverage   84.13%   84.27%   +0.14%     
==========================================
  Files         199      201       +2     
  Lines       20164    20386     +222     
  Branches     1063     1073      +10     
==========================================
+ Hits        16964    17181     +217     
- Misses       3010     3014       +4     
- Partials      190      191       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@rytilahti rytilahti left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks a lot for this PR, @Acrobot! Now that I have a real device to test against, I did some rewiring based on how I felt it should behave.

I did quite a few cleanups to keep it simple (and not bound to homeassistant specifics like device_classes and icons), but this might be extended later. I am planning to do some more improvements to stress test the design & to add proper support for my airmonitor (urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgd1st:1), but I will save the improvements into separate PRs.

@rytilahti
rytilahti merged commit f07f5f3 into rytilahti:master Jul 21, 2026
14 checks passed
@Acrobot
Acrobot deleted the genericmiot-metadata branch July 22, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants