# Zigbee2mqtt types
This is an auto generated lib containing concrete types that can easily deserialise any json MQTT payloads
from zigbee2mqtt.
## How to use
To speed up compile time all structs are behind feature flags, these are split into vendors e.g. if you are using a
sensor by xiaomi then you will need to add the xiaomi feature to your Cargo.toml like `zigbee2mqtt-types = {features = ["xiaomi"]}`
Example of using [a door contact sensor](https://www.zigbee2mqtt.io/devices/MCCGQ11LM.html) (note: debug is also a feature)
add `zigbee2mqtt-types = {features = ["debug", "xiaomi"]}` to Cargo.toml dependencies
```rust
use zigbee2mqtt_types::xiaomi::ZigbeeMccgq11lm;
#[test]
fn contact_sensor_mccgq11lm() {
//https://www.zigbee2mqtt.io/devices/MCCGQ11LM.html
let json = serde_json::json!({
"voltage": 2995,
"battery": 97,
"device_temperature": 19,
"power_outage_count": 6,
"linkquality": 247,
"contact": false,
})
.to_string();
let parsed: ZigbeeMccgq11lm =
match serde_json::from_str(&json) {
Ok(contact) => contact,
Err(err) => {
println!("{:?}", err);
assert!(false);
}
};
assert_eq!(2995, parsed.voltage);
assert_eq!(97, parsed.battery);
assert_eq!(19, parsed.device_temperature);
assert_eq!(6, parsed.power_outage_count);
assert_eq!(247, parsed.linkquality);
assert_eq!(false, parsed.contact);
}
```
Zigbee2MQTT has two [additional options](https://www.zigbee2mqtt.io/guide/configuration/mqtt.html#mqtt-behaviour) that add
data to the JSON, 1) elapsed, 2) last_seen. There is a feature flag to also convert these to their static types
(**Note**: Atm I am converting timestamps to String, I didn't want to way into converting the string into a real time struct I'm sorry.
PR welcome, but I fear devils are awaiting)
1) Elapsed can be enabled with feature = `elapsed` and it gets converted to a `elapsed: u64`
2) Last_seen can be iso_8601(local and utc) or epoch. Note: A parse error will be thrown if these features are enabled but the field is not present in the json
- For either of the iso use feature = `last_seen_iso_8601` and it gets converted to a `last_seen: String`
- For epoch use feature = `last_seen_epoch` and it gets converted to a `last_seen: u64`
- **Note**: last_seen features are mutually exclusive as they both resolve to the same struct field just with different types, this does break Rust convention but I see no real use case that both would be enabled. If you have one please open an issue and I can look at a way to make it not mutually exclusive
These features manifest like so
```rust
use zigbee2mqtt_types::xiaomi::ZigbeeMccgq11lm;
#[test]
fn contact_sensor_mccgq11lm_last_seen_iso_8601_elapsed() {
//https://www.zigbee2mqtt.io/devices/MCCGQ11LM.html
let json = serde_json::json!({
"voltage": 2995,
"battery": 97,
"device_temperature": 19,
"last_seen": "2022_10_20T11:55:07.199z", // zigbee2mqtt last_seen setting in ISO_8601 mode
"power_outage_count": 6,
"linkquality": 247,
"contact": false,
"elapsed": 2547593 // zigbee2mqtt elapsed setting true
})
.to_string();
let parsed: ZigbeeMccgq11lm = match serde_json::from_str(&json) {
Ok(contact) => contact,
Err(err) => {
println!("{:?}", err); // with last_seen or elapsed feature flags turned on a parse errr will be thrown if not present in the json
assert!(false);
}
};
assert_eq!("2022_10_20T11:55:07.199z", parsed.last_seen); // last_seen parsed to a string
assert_eq!(2547593, parsed.elapsed); // elapsed parsed to a u64
assert_eq!(2995, parsed.voltage);
assert_eq!(97, parsed.battery);
assert_eq!(19, parsed.device_temperature);
assert_eq!(6, parsed.power_outage_count);
assert_eq!(247, parsed.linkquality);
assert_eq!(false, parsed.contact);
}
```
## Some gotchas
### Model/Vendor names
1 gotcha is all not all model/vendor names strings conform to Rusts struct/package naming convention, so I needed
todo some transformation to them. Models roughly remove all invalid chars and have Zigbee infront of them. Vendors have
an `_` in place of any invalid chars
example of changes to models
```txt
"ZS057-D0Z" = "ZigbeeZs057Dd0z"
"BF 265" = "ZigbeeBf265"
"5110.40" = "Zigbee5110F40"
"MEAZON_BIZY_PLUG" = "ZigbeeMeazonUbizyUplug"
```
example of changes to vendors
```txt
"Eaton/Halo LED" = "eaton_halo_led"
"Custom devices (DiY)" = "custom_devices__diy_"
"Villeroy & Boch" = "villeroy___boch"
"J.XUAN" = "j_xuan"
// the bellow is just removing non ascii chars I would personally prefer to swap them for similar ascii but for now I will _
"Müller Licht" = "m_ller_licht"
"Sinopé" = "sinop_"
```
Model function that does conversion is [here](https://gitlab.com/seam345/zigbee2mqtt-types/-/blob/2966e202ede466085392acbeea77c647c508dd36/src/bin/generate.rs#L214)
Vendor function that does conversion is [here](https://gitlab.com/seam345/zigbee2mqtt-types/-/blob/2966e202ede466085392acbeea77c647c508dd36/src/bin/generate.rs#L77)
### Composite values
I currently don't parse object values so color on the philips light in mqtt it is `"color": {"hue":25,"saturation":95,"x":0.5267,"y":0.4133}`,
in short anything with a `{` in the value is not parsed. This will be fixed in a future update I just wanted to reduce scope
## Why
While creating a recent project I found myself being lazy with how I was defining my structs to consume MQTT payloads
and given they are already defined in Zigbee2MQTT I went looking for a way to generate them programmatically.
## Contributions
All development should be in the top level generator binary as [zigbee-herdsman](https://github.com/Koenkk/zigbee-herdsman-converters.git)
updates too frequently to maintain human updating of this lib.
Atm I don't have this generating wit CI but at some point I plan to that to keep this up to date with [zigbee-herdsman](https://github.com/Koenkk/zigbee-herdsman-converters.git)
## Future goals
- [ ] improve lib documentation
- [ ] some sort of type extension to allow last seen to be correctly parsed
- [ ] add get set methods
Up to date issues can be seen here https://gitlab.com/seam345/zigbee2mqtt-types/-/issues