Package: hss-skill
HSS - SkillLibrary for creating skills based on theHermes Skill Server.A Node.JS library is also available, check outHSS-Skill.InstallationSimply usepip:(hss) pi@ceres:~/development/myskill $ pip3 install hss_skillOverviewThehss_skillpackage contains tools for fast and easy development of skills for theHermes Skill Server. The goal is to let skill developers only care about their own skill implementation, while the internal stuff (communication with the skill-server, reading configuration, etc) is provided out-of-the-box by thehss_skillpackage.The package provides a base class for skillsBaseSkillwhich does all the incovenient stuff, like communication with the skill server, reading configuration file etc.Getting startedYour skill implementation must provide the following components:installedhss_skillmodulemain.pyfile as entrypointskill.jsonfile containing meta infos about your skillyour skill implementation (e.g.myskill.py)requirements.txtfile containing python dependencies, at leasthss_skill[optional]config.ini.defaultfile containing your skill's configuration (default) parametersIn addition, for interaction withrhasspyvoice assistant:[optional]sentences.inicontaining the sentencesrhasspyshould use (only YOUR SKILLs sentences)[optional]slots.jsoncontaining slot definitions your skill usesIfsentences.iniis provided,hss-cliwill register the sentences atrhasspyupon skill installation, and triggerrhasspyfor training.Same applies toslots.json.In order to support multiple languages, a skill may have the following files instead ofslots.jsonandsentences.ini:slotsdict.[LANG].json(multiple, one per language withLANGbeeing the language code in lower case, e.g. "de_de")sentences.[LANG].ini(multiple, one per language withLANGbeeing the language code in lower case, e.g. "de_de")BoilerplateYourmain.pymight be sufficient if it looks roughly like this:import myskill

if __name__ == "__main__":
    skill = myskill.MoodSkill()
    skill.run()Yourrequirements.txtcould look like:hss_skill>=0.4.2
certifi
geopy>=1.20.0
requests>=2.22.0Your skill implementationWhen developing skills, a subclass ofBaseSkillmustimplement thecoroutine:async def handle(request, session_id, site_id, intent_name, slots, mapped_slots)A coroutine which is called every time an intent which was registered by your skill is recognized and should be answered.Usually, the parametersintent_nameandslotsmight be sufficient, however the full original intent is provided in therequestparameter, andsession_idandsite_idcan be used to do session- and site-based intent handling.The implementation of this method shouldusuallyreturn with the execution of eitherBaseSkill.answerorBaseSkill.followupto finish intent handling (see below).To support multiple languages with one skill implementation,mapped_slotsis provided, which contains each slot translated to a slot-identifier (instead of the raw slot string). See chapter "Multiple languages support".ExampleA minimal example of a skill (myskill.py) might look as:from hss_skill import hss

class MoodSkill(hss.BaseSkill):
    def __init__(self):
        super().__init__()   # important, call super's constructor

    async def handle(self, request, session_id, site_id, intent_name, slots):
        return self.answer(session_id, site_id, "Thanks, I am fine")Contents ofskill.jsonTheskill.jsonis a mandatory file containing meta info about your skill. It is used both during installation as well as when your skill is run.It could look like the following:{
    "platform": "hss-python",
    "type": "weather",
    "name": "hss-s710-mood",
    "version": "1.0.0",
    "author": "Some Dude",
    "intents": ["s710:howAreYou"],
    "shortDescription": "Some funny chatting",
    "version": "1.0.0",
    "language": "en_GB"
}Properties explained:platform(mandatory)Must behss-python, stating the skill is a python based HSS skill.type(mandatory)Type of skill, e.g.weather. Must be one of:weathercalendarmusicdatetimenewsgamesfunutilityautomationversion(mandatory)The version number of the skill.author(mandatory)The name of the author of the skill.intents(mandatory)An array of strings containing all intents the skill can handle.shortDescription(mandatory)A short description of your skill. Will be shown in the HSS registry skill list.version(optional)A string describing your skill's version.language(mandatory)A four-letter code string determining your skill's default language. When the skill supports more than one language, this property shall be an array (e.g.["de_DE", "en_GB"]).Base classIn addition,BaseSkillprovides several methods and properties which aid in skill development.BaseSkill.logLogger object which can be used for logging.BaseSkill.default_languageThe default language as determined by theBaseSkillclass.Can be changed by the skill implementation any time to affect the behaviour of the below mentioned methods.The default language is determined in the follow way (and order):config.ini sectionskillparameterlanguageskill.jsonlanguageproperty (first language, iflanguageis an array)fall back toen_GBdef answer(session_id, site_id, response_message, lang)Theanswer-method should be called after the intent has been fully handled. This method also allows to send a response-text, which will then be forwarded to the TTS the your voice assistant.The parameterssession_idandsite_idshould be the ones provided byhandle, while thetextparameter shall be the text which shall be asked by the voice assistant.If thelangparameter is not given,BaseSkill.default_languagewill be used.def followup(session_id, site_id, question, lang, intent_filter = None)Thefollowup-method should be called when the skill does not yet want to finish handling, but instead needs to ask for additional input. Thequestion-text will be forwarded to the TTS of the voice assistant. In addition, a filter for intents (array of strings) can be given (seehermes protocol docs).The parameterssession_idandsite_idshould be the ones provided byhandle, while thequestionparameter shall be the text which shall be asked by the voice assistant.If thelangparameter is not given,BaseSkill.default_languagewill be used.async def say(text, siteId = None, lang = None)Thesaycoroutine can be used to trigger the voice assistant to say a given text using its TTS. There is no further session- or intent handling involved.If thelangparameter is not given,BaseSkill.default_languagewill be used.Sincesayis acoroutine, it must beawait-ed.async def ask(text, siteId = None, lang = None, intent_filter = None)Theaskcoroutine can be used to start a new session. This will usually cause the voice assistant to speak the providedtextusing its TTS, and then listen for intents. Recognized intents may then be processed again.If thelangparameter is not given,BaseSkill.default_languagewill be used.Optionally, anintent_filter(array of strings) can be given which will be forwarded to the voice assistant (seehermes protocol docs).Sinceaskis acoroutine, it must beawait-ed.TimersTheBaseSkillclass provides a convenience method for setting up timers, which will execute a given callback function after a given timeout. This might be useful if the skill wants to trigger actions on its own at a given time.Currently, as a limitation, only one timer can be active at a time. This will most likely change in the future, so that an arbitrary number of timers can be scheduled.For this, two coroutines are provided:async def timer(timeout, callback, user = None, reschedule = False)Schedules a new timer.timeoutshall beintand denote the numer of seconds until the providedcoroutinecallbackis executed. Ifuseris given, it will be passed tocallbackupon execution.If a timer is already running, new scheduling will fail unlessTrueis given forreschedule. In the latter case, the previous timer will be cancelled before a new timer is scheduled.async def cancel_timer(strict = True)Cancels an existing timer. IfTrueis given forstrict, an error message will be printed whencancel_timeris called but no timer is running.Exampleasync def handle(self, request, session_id, site_id, intent_name, slots, mapped_slots):

        ... # skill handling code

        # schedule timer in 10 seconds

        await self.timer(10, self.do_timer, "Can I ask you a question?", reschedule = True)

        # finish intent handling

        return self.answer(session_id, site_id, response_message)

    async def do_timer(self, text):

        # ask a question

        await self.ask(text, siteId = "default", intent_filter = ["s710:confirm", "s710:reject"])Multiple languages supportIn order to support more than one language, a skill might need the following:sentences in all supported languagesslots in all supported languagesdetermination of current/active languageeasy handling of language specific slots in the codehss-skillsupports all of this, thus enabling developers to easily implement more than one language in their skills.sentences.iniIn order to support more than one language, instead of providing a file namedsentences.ini, provide one file per language, and include the language code (lowercase) in the filename:sentences.de_de.inisentences.en_gb.inislotsIn order to support more than one language, instead of providing a file namedslots.json, provide one file per language, and include the language code (lowercase) in the filename. Also, the file does not contain arrays, but dictionary instead:slotsdict.de_de.jsonslotsdict.en_gb.jsonThe idea is, that it will be cumbersome to work with the localized, language-specific slot strings in the code, when multiple languages are involved. Therefore, theslotsdict.jsonfiles provide a mapping from localized ("real") slots strings to slot-identifiers, which will be the same for every language. Those slot-identifiers will be provided to thehandle()method in themapped_slotsparameters.The files might contain:slotsdict.de_de.json:

{
   "relative_time": {
      "now": ["jetzt", "gerade", "später", "nachher", "gegen später"],
      "today": ["heute"],
      "todayMorning": ["heute früh", "heute morgen"]
   }
}

slotsdict.en_gb.json:

{
   "relative_time": {
      "now": ["now", "right now", "later", "then", "towards later", "around later"],
      "today": ["today"],
      "todayMorning": ["this morning", "ealier today"]
   }
}So, if, for example, a slot with the text "nachher" is recognized (with german language enable),handle()will receivenachherin theslotsparameter, andnowin themapped_slotsparameter.If a slot with the text "right now" is recognized (with english language enable),handle()will receiveright nowin theslotsparameter, andnowin themapped_slotsparameter.This means, that the skill implementation can rely on a language-independent slot-identifer (nowin the above example) while still having access to the original, language-specific slot value (nachher/right now).When installing a skill with the above slot-dictionaries,hss-cliwill still register slots as usual at the voice assistant. It will, however, only register the slots for the language which is selected upon installation.ConfigurationIf your skill needs its own configuration parameters which must be supplied by the user (e.g. access tokens, ...), you can provide aconfig.ini.defaultfile.This file is meant to a) give default values for configuration options and b) contain empty configuration values, which must be filled by the user upon skill installation. SeeHermes Skill Serverfor details about skill installation.Upon installationconfig.ini.defaultwill be copied intoconfig.ini, and values will be filled by the user.config.ini.defaultwill remain untouched.Example[skill]
confirmation = I am okay, what about you?In code, you can access the configuration using theBaseSkill'scfgmember. It will be a dictionary object resembling your configuration.async def handle(self, request, session_id, site_id, intent_name, slots):
        return self.answer(session_id, site_id, self.cfg["skill"]["confirmation"])Skill installationPlease refer toHermes Skill Server.