esphome-miot
esphome-miot copied to clipboard
Add example `mmgg.feeder.fi1` config
This PR adds complete support for mmgg.feeder.fi1 Pet Feeder.
It requires the Event Component PR (#16) to achieve feature parity with the stock firmware, so please review that before merging this.
I'll describe the Miot services first one at a time, and afterwards go through what I've had to do to replicate features implemented in the xiaomi cloud instead of on-device.
For the future
The only things remaining that I'd like implemented, but can be handled outside this PR are:
- Automatic DST time-zone adjustment of the schedule.
When daylight savings is in effect, the
phon-timezone(SIID9/PIID12) can be updated by automation in order to preserve the preset dispense times. This is something that also the stock setup tries to handle (horribly, it required a factory reset each spring/autumn), but I've left aTODOcomment about this for the future. Not doing anything about DST is already an improvement. More details in the corresponding service sections. - HomeAssistant UI control of feeding schedule.
There isn't a "standard" ESPHome <--> HomeAssistant UX for controlling daily scheduled events in a nice way, so I've opted to leave the schedule format (
feddplan-string) untouched and handle display and editing of feeding times to a custom HA card. Again, more details in the proper section.
ESP8266 UART Quirk
This devices uses an ESP8266, which can't reliably handle 2 UARTs at the same time. The built-in serial logger must be disabled
logger:
baud_rate: 0
in order to avoid occasional errors like this:
Timeout while receiving from MCU, dropping message 'get_do???'
Mi IoT Spec implementation
General observation: Parameters that are listed as input or output of actions are not usable by themselves. They only appear in the result message of an action or used in the action message.
Device Info (SIID: 1)
Nothing is sent on 1:1 ~ 1:5 on power cycle.
Feeder (SIID: 2)
Properties
faultworks as expectedfeeding-measureCan be read, but produces garbage. Writing to it directly has no effect. Used as input for thepet-food-outaction.pet-food-left-levelWorks as expected
Actions
pet-food-outWorks as expected. Dispenses the specified portions immediately. Optionalfeeding-measureparameter; if left out, dispenses one portion.
Indicator Light (SIID: 3)
Properties
onRead/Write works as expected
Feed Service (SIID: 4)
Properties
outfood-num. Number of dispensed portions in last event. Specs say this isread|notify, but really it's onlynotify. The feed started|status|successful events use this to communicate what happenedoutfood-id. Index of scheduled feed that dispensedoutfood-numportions in last event, or255if a manual feed. Specs say this isread|notify, but really it's onlynotify. The feed started|status|successful events use this to communicate what happenedoutletstatus. There is a motorized door protecting the dispensing outlet, "for freshness". Can be read. Is notified byproperties_changed, but only after it's closed; there's no notification when it's opened, not even in the "Feeding Started" event.doorstatus. Top Lid of the device. Works as expected, thankfully.
Events
feedsuccesswhen feeding is done, withoutfood-idrepresenting the trigger andoutfood-numthe total.feedstatsrepeatedly while dispensing, withoutfood-idrepresenting the trigger andoutfood-numthe current progress of portions.feedstartwhen starting the feed, with onlyoutfood-idupdated.
Feeding Schedule (SIID: 5)
There's a 10 slot schedule stored on device for offline / battery powered dispensing, controlled by this service, in combination with the phon-timezone property of the device control service.
One slot is sent as [int id],[int hour],[int minute],[int portions],[int status] where:
id-0-9- ID of the slothour-0-23- Hour of the slot, as UTC+phon-timezone(e.g. ifphon-timezoneis2, a value of12here means10 UTC).minute-0-59Minute of the slotportions-1-30Number of portions to be dispensedstatus- Status of the slot at the time of querying. Observed values:0Dispensed successfully.1Failed to dispense (food stuck, etc.)254Dispensing in progress.255Pending.
An empty slot is returned as [int id],255,255,255,255
Every day at midnight all statuses reset from 0 Dispensed/1 Failed back to 255 Pending. I didn't have a chance to check if this happens in UTC or UTC+phon_timezone
Example: 2,12,30,5,0 - Slot 2, at 12:30 dispense 5 portions, dispensed successfuly for today.
The time order of the slots does not matter, i.e. slot 1 can be 1pm while slot 2 9am, and 2 will be dispensed before 1.
There can be empty slots between filled slots without errors.
Ther entire schedule can be queried in two steps by the getfeedplan action.
Properties
feedplan-contr. Enable or disable the automatic feeding schedule. Works as expected.feedplan-hour- named parameter forfeedlist{add,edit,del}actionsfeedplan-min- named parameter forfeedlist{add,edit,del}actionsfeedplan-unit- portions to be dispensed, named parameter forfeedlist{add,edit,del}actionsfeedplan-stat- unused. Seems to be thestatusof the schedule item, but setting it in afeedlist-action does not override the item's status.feedplan-id- named parameter forfeedlist{add,edit,del}actionsgetfeedplan-num- named parameter forgetfeedplanaction to control which "page" to returnfeddplan-string- named return value of thegetfeedplanaction.
Actions
-
stopfeed. Unknown, didn't try. Marksfeedplan-contras named parameter, but the control is switchable by itself, making this action redundant. -
getfeedplan- Get schedule. Usageaction 5 6 11 0oraction 5 6 11 1, and returns the first 5 or last 5 schedules in thefeddplan-stringproperty as output. Example:// Get page 0 of schedule. (schedules 0-4) [15:04:28][V][miot:189]: Sending reply 'down action 5 6 11 0' to MCU [15:04:28][V][miot:095]: Received MCU message 'result 5 6 0 12 "0,6,0,5,0,1,10,0,5,0,2,255,255,255,255,3,255,255,255,255,4,255,255,255,255"' // Get page 1 of schedule. (schedules 5-9) [15:05:06][V][miot:189]: Sending reply 'down action 5 6 11 1' to MCU [15:05:06][V][miot:095]: Received MCU message 'result 5 6 0 12 "5,255,255,255,255,6,255,255,255,255,7,255,255,255,255,8,255,255,255,255,9,255,255,255,255"'In order to expose all 10 slots in one sensor, I keep each individual slot in a global
std::map<std::string, std::string>, updated whenever thefeddplan-stringchanges, and reconstruct the full string from it in theraw_feed_plantemplate sensor. I also exclude the empty slots from it for brevity's sake.Also, to always have the statuses up to date, I refresh this sensor at the
feedstartandfeedsuccessevents and after midnight.- Also exposed to HA as
refresh_feed_planservice
- Also exposed to HA as
-
feedlistadd- add a new schedule to slotfeedplan-id. Specifying an already occupied slot id edits it without an error.- exposed to HA as
add_scheduled_feedservice
- exposed to HA as
-
feedlistedit- edit the schedule in slotfeedplan-id- exposed to HA as
edit_scheduled_feedservice
- exposed to HA as
-
feedlistdel- clear the schedule in slotfeedplan-id.- exposed to HA as
remove_scheduled_feedservice
- exposed to HA as
Child Lock (SIID: 6)
Everything works as expected.
Cleaning Reminder (SIID: 8)
Everything works as expected.
Device Control (SIID:9)
Properties
-
factory-result. Did not test. Specs sayread|notifybut I expect is only an output for theprtestaction -
phon-time-zone. GMT offset in hours of the schedule times. It's effectively the "Device time zone".Changing this number does not also modify the hour numbers set in the schedule, but affects the actual dispensing time.
Also, since this is an integer, users in countries with 30/45 minute TZ offet will never see the proper time.
-
countrycode. Unknown if this is useful. Possibly related to the server (de/cn/us/etc) and region-locking. Values unknown, mine returns to3after a minute whatever I set it toread|write|notifyworks.
Actions
prtestdid not test. I didn't want to risk damage to my device, as I don't know under what conditions this needs to be performed in the factory.set-phone-time-zonedid not test. The mentionedphon-time-zonecan be set directly.set-countrycodehas the same effect as setting thecountrycodedirectly, and for my device it always returns to3eventually.
Events
-
wifioffwall power loss, ESP will be powered off in 30s to preserve battery.If the power is restored within the 30s there is no further event sent and nothing happens.
I tried making use of this to gracefully shutdown the esp, but if the power is restored before it's powered off it would be stuck in the poweroff state.
Replace Dessicant Reminder (SIID: 11)
Everything works as expected.
Extra features
I've added a dispense_food_portions template number to control how much is dispensed by the dispense_food button, for easier usage.
Also available in HA as a dispense_food service
The Portions Dispensed Today total counter is also implemented on-device. After each feedsuccess event the counter is incremented, and reset at midnight.
Device Screenshots
The last screenshot demonstrates a HA-side custom card to view/edit the schedule in the UI.
I'll try to publish it separately once it's finished, but with this, the device should be fully controllable in Home Assistant.
Sorry for the delay, I'm a bit swamped right now. I'll try to look at this soon ;)
Sorry for the delay, I'm a bit swamped right now. I'll try to look at this soon ;)
Don't worry about it, we're not on a deadline :)
I hope you don't mind me editing this and the event_occurred PR with new functionality in the meantime, I ironed out some more kinks.
I'll keep this as a draft a bit more as I add the final missing pieces for 100% feature parity.
Hey @dhewg , I hope you're doing well.
I finally had some time to finish the code, and more importantly the mountain of text in the PR.
Sorry about that, but it's a large change after all, and I tried to document the implementation as best as possible.
It's ready for review when you have time.
Cheers!
Hey there, things are slowly getting back to normal, sorry for the massive delay! I'll hope to look at the open PRs soon, thanks for keeping to work on it!
Hm, that github web interface thingy to resolve the conflics didn't work somehow...
Anyway, I finally merged this. Thanks for the work and especially for the patience!
The events are a little hacky, but if we get a nicer event component we can adapt this config afterwards