Skip to content

Commit

Permalink
Add preliminary support for managing sound files (#154)
Browse files Browse the repository at this point in the history
* Add preliminary support for managing sound files

Adds install_sound which expects url, md5sum and id

* Finalize sound installation

* sound_install works now, displays installation state during installation
* SoundInstallStatus and SoundStatus are new sound-related containers
* get_sound_volume returns now an integer, requires a recent FW

* add documentation for installing sound packs

* add a link to dustcloud's audio generator, revise a bit
  • Loading branch information
rytilahti committed Jan 18, 2018
1 parent 52bc251 commit 8032eec
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 3 deletions.
26 changes: 26 additions & 0 deletions docs/vacuum.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Following features of the vacuum cleaner are currently supported:
is not currently implemented, patches welcome!**
- Fetching and setting the schedules.
- Setting and querying the timezone.
- Installing sound packs.
- Manual control of the robot. **Patches for a nicer API are very welcome.**

Use :ref:`mirobo --help <HelpOutput>`
Expand Down Expand Up @@ -126,6 +127,31 @@ Cleaning history
Duration: (0:23:54)


Sounds
~~~~~~

To get information about current sound settings:

::

mirobo sound


You can use dustcloud's `audio generator`_ to create your own language packs,
which will handle both generation and encrypting the package for you.

To install your newly generated sound pack, you have to host it somewhere accessible to the vacuum,
and you have to know its md5sum.
The last parameter to give to the command is sound id (or `sid`),
which you can choose by yourself.

::

mirobo install_sound http://10.10.20.1:8000/my_sounds.pkg b50cfea27e52ebd5f46038ac7b9330c8 1005


.. _audio generator: https://github.com/dgiese/dustcloud/tree/master/xiaomi.vacuum.gen1/audio_generator

DND functionality
~~~~~~~~~~~~~~~~~

Expand Down
26 changes: 24 additions & 2 deletions miio/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import pytz

from .vacuumcontainers import (VacuumStatus, ConsumableStatus, DNDStatus,
CleaningSummary, CleaningDetails, Timer)
CleaningSummary, CleaningDetails, Timer,
SoundStatus, SoundInstallStatus)
from .device import Device, DeviceException

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -214,7 +215,28 @@ def fan_speed(self):

def sound_info(self):
"""Get voice settings."""
return self.send("get_current_sound")
return SoundStatus(self.send("get_current_sound")[0])

def install_sound(self, url: str, md5sum: str, sound_id: int):
"""Install sound from the given url."""
payload = {
"url": url,
"md5": md5sum,
"sid": int(sound_id),
}
return SoundInstallStatus(self.send("dnld_install_sound", payload)[0])

def sound_install_progress(self):
"""Get sound installation progress."""
return SoundInstallStatus(self.send("get_sound_progress")[0])

def sound_volume(self) -> int:
"""Get sound volume."""
return self.send("get_sound_volume")[0]

def set_sound_volume(self, vol: int):
"""Set sound volume."""
raise NotImplementedError("unknown command&parameters")

def serial_number(self):
"""Get serial number."""
Expand Down
26 changes: 25 additions & 1 deletion miio/vacuum_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import sys
import json
import ipaddress
import time
from pprint import pformat as pf
from typing import Any # noqa: F401

Expand Down Expand Up @@ -423,9 +424,32 @@ def cleaning_history(vac: miio.Vacuum):
@pass_dev
def sound(vac: miio.Vacuum):
"""Query sound settings."""
click.echo(vac.sound_info())
click.echo("Current sound: %s" % vac.sound_info())
click.echo("Current volume: %s" % vac.sound_volume())
click.echo("Install progress: %s" % vac.sound_install_progress())


@cli.command()
@click.argument('url')
@click.argument('md5sum')
@click.argument('sid', type=int)
@pass_dev
def install_sound(vac: miio.Vacuum, url: str, md5sum: str, sid: int):
"""Install a sound."""
click.echo("Installing from %s (md5: %s) for id %s" % (url, md5sum, sid))
click.echo(vac.install_sound(url, md5sum, sid))

progress = vac.sound_install_progress()
while progress.is_installing:
print(progress)
progress = vac.sound_install_progress()
time.sleep(0.1)

if progress.progress == 100 and progress.error == 0:
click.echo("Installation of sid '%s' complete!" % progress.sid)
else:
click.echo("Error during installation: %s" % progress.error)

@cli.command()
@pass_dev
def serial_number(vac: miio.Vacuum):
Expand Down
67 changes: 67 additions & 0 deletions miio/vacuumcontainers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: UTF-8 -*#
from datetime import datetime, timedelta, time
from typing import Any, Dict, List
from enum import IntEnum
import warnings
import functools
import inspect
Expand Down Expand Up @@ -443,3 +444,69 @@ def action(self) -> str:

def __repr__(self) -> str:
return "<Timer %s: %s - enabled: %s - cron: %s>" % (self.id, self.ts, self.enabled, self.cron)


class SoundStatus:
"""Container for sound status."""
def __init__(self, data):
# {'sid_in_progress': 0, 'sid_in_use': 1004}
self.data = data

@property
def current(self):
return self.data['sid_in_use']

@property
def being_installed(self):
return self.data['sid_in_progress']

def __repr__(self):
return "<SoundStatus current: %s installing: %s>" % (
self.current,
self.being_installed)


class SoundInstallState(IntEnum):
Unknown = 0
Downloading = 1
Installing = 2
Installed = 3
Error = 4


class SoundInstallStatus:
"""Container for sound installation status."""
def __init__(self, data):
# {'progress': 0, 'sid_in_progress': 0, 'state': 0, 'error': 0}
self.data = data

@property
def state(self) -> SoundInstallState:
"""Installation state."""
return SoundInstallState(self.data['state'])

@property
def progress(self) -> int:
"""Progress in percentages."""
return self.data['progress']

@property
def sid(self) -> int:
"""Sound ID for the sound being installed."""
# this is missing on install confirmation, so let's use get
return self.data.get('sid_in_progress', None)

@property
def error(self) -> int:
"""Error code, 0 is no error, other values unknown."""
return self.data['error']

@property
def is_installing(self) -> bool:
"""True if install is in progress."""
return self.sid != 0 and self.progress < 100 and self.error == 0

def __repr__(self) -> str:
return "<SoundInstallStatus sid: %s (state: %s, error: %s)" \
" - progress: %s>" % (self.sid, self.state,
self.error, self.progress)

0 comments on commit 8032eec

Please sign in to comment.