Heaty will die, Schedy be born!

Hi Everyone,
At the beginning I would like to thanks (@roschi) for such great work :slight_smile:
This is my very beginning with Home Assistant, Schedy and python so please be gentle.
I’ve successfully configured Schedy with thermostat actors.
No I’m trying to use generic actor without success.

I’ve got the following error message:

2019-01-30 22:39:03.403069 ERROR PurifierOnOff: !!! [R:Purifier] [A:fan.xiaomi_air_purifier_salon] The value ('on',) has not the expected number of items (0)

My configuration looks as below:

PurifierOnOff:
  module: hass_apps_loader
  class: SchedyApp
  debug: true
  
  actor_type: generic
  actor_templates:
    generic:
      attributes:
        - attribute: "state"
          values:
            'off':
              service: "fan/turn_off"
            'on':
              service: "fan/turn_on"

  watched_entities:
    - "input_boolean.away"

  rooms:
    living:
      friendly_name: Purifier
      actors:
        fan.xiaomi_air_purifier_salon:
      schedule:

        - v: 'on'
          rules:
            # don't turn on when 'Off' mode is selected
            - x: "Break() if state('input_select.purifier_mode') == 'Off' else Skip()"
            # don't turn on when 'away' mode is selected
            - x: "Break() if is_on('input_boolean.away') else Skip()"
            # don't turn on when pm2.5 < 10
            - x: "Break() if float(state('sensor.salon_pm25') or 0.0) < 10.0 else Skip()"
            # on weekends and holidays
            - rules:
              - x: "Skip() if is_on('input_boolean.holidays') else Break()"
                weekdays: "!6-7"
              - { start: "00:00", end: "00:00" }
            # on normal working days, turn on from 14:30 to 7:00
            - weekdays: 1-5
              rules:
              - { start: "14:30", end: "07:00" }
            # on weekends
            - weekdays: 6-7

        - v: 'off'
          rules:
            # don't turn off when 'Auto' mode is selected
            - x: "Break() if state('input_select.purifier_mode') == 'Auto' else Skip()"
            # don't turn off when 'Silent' mode is selected
            - x: "Break() if state('input_select.purifier_mode') == 'Silent' else Skip()"
            # don't turn on when pm2.5 > 5
            - x: "Break() if state('input_select.purifier_mode') == 'Silent' and float(state('sensor.salon_pm25') or 0.0) > 5.0 else Skip()"
            - weekdays: 1-7

      watched_entities:
        - "sensor.salon_pm25"
        - "input_select.purifier_mode"
        - "input_boolean.holidays"

The question is: what is wrong with configuration? Am I missing something? I read the documentation and I’m not sure what else I have to do.

BR,
Seba

EDIT: Ok, I’ve found the error. In actor_template section I have to use “default” instead of generic. Thanks again for great work.

Hi @SebuZet

Cool that someone is using the generic actor. I guess you know that you’ve actually just re-implemented the switch actor? Unless you want to add more slots, such as fan speed etc., you can simply use the switch actor instead.

And yes, the template names aren’t related to the actor type. The default template is just the one all actors inherit from, something like the great grandfather of everything :slight_smile: .

Best regards
Robert

Just two more notes.

  1. I’d suggest you only implement the sub-schedule with checks for the on branch and use off with no sub-schedule as fallback as the last rule. That would simplify it greatly.

  2. You could move the living room schedule to schedule_prepend or create a schedule snippet, use the room_name variable in your expressions to construct entity ids and thus make it generic.

Hi Robert,

Yes, I know. I’m just warming up :wink:
The goal is to implement handling purifier speeds and modes.
Yesterday I’ve started with implementing (modifying thermostat) new type of actor but then I realized that using generic type of actor will be enough for me.
With this approach I will have less problems with updating to hass-apps and, what is most important, I don’t have to start learning python today.

I’ll take into account your suggestions. I’ll let you know what is the status.
Thanks for suggestions,

BR,
Seba

Ok, wish you good luck! Just ask if you’ve got questions. You could use the custom actor type as well if your requirements grow, although there are no usage examples yet.

Thanks,

Work is in progress and I think I need a little help.
I’ve got error like this:

: --- Validating the app's configuration. stdout

11:41:37 2019-01-31 12:41:37.076684 WARNING AppDaemon: ------------------------------------------------------------ stderr

11:41:37 2019-01-31 12:41:37.077112 WARNING AppDaemon: Unexpected error running initialize() for PurifierOnOff stderr

11:41:37 2019-01-31 12:41:37.077375 WARNING AppDaemon: ------------------------------------------------------------ stderr

11:41:37 2019-01-31 12:41:37.078702 WARNING AppDaemon: Traceback (most recent call last): stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/appdaemon/appdaemon.py", line 1581, in init_object stderr

11:41:37 init() stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/hass_apps/common.py", line 90, in initialize stderr

11:41:37 self.cfg = self.Meta.config_schema(cfg) # pylint: disable=not-callable stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/schema_builder.py", line 267, in __call__ stderr

11:41:37 return self._compiled([], data) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/validators.py", line 204, in _run stderr

11:41:37 return self._exec(self._compiled, value, path) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/validators.py", line 286, in _exec stderr

11:41:37 raise e if self.msg is None else AllInvalid(self.msg, path=path) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/validators.py", line 284, in _exec stderr

11:41:37 v = func(path, v) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/schema_builder.py", line 811, in validate_callable stderr

11:41:37 return schema(data) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/hass_apps/schedy/config.py", line 96, in config_post_hook stderr

11:41:37 actor_data = vol.Schema(actor_type.config_schema_dict)(_actor_data) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/schema_builder.py", line 267, in __call__ stderr

11:41:37 return self._compiled([], data) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/schema_builder.py", line 589, in validate_dict stderr

11:41:37 return base_validate(path, iteritems(data), out) stderr

11:41:37 File "/usr/local/lib/python3.6/site-packages/voluptuous/schema_builder.py", line 427, in validate_mapping stderr

11:41:37 raise er.MultipleInvalid(errors) stderr

11:41:37 voluptuous.error.MultipleInvalid: extra keys not allowed @ data['attributes'][1]['values']['Auto']['value_param'] stderr

With this config:

PurifierOnOff:
  module: hass_apps_loader
  class: SchedyApp
  debug: true
  
  actor_type: generic
  actor_templates:
    default:
      attributes:
        - attribute: "state"
          values:
            'off':
              service: "fan/turn_off"
            'on':
              service: "fan/turn_on"
        - attribute: "mode"
          values:
            'Auto':
              service: "fan/set_speed"
              value_param: "speed"
            'Silent':
              service: "fan/set_speed"
              value_param: "speed"
            'Favorite':
              service: "fan/set_speed"
              value_param: "speed"
        - attribute: "favorite_level"

  schedule_snippets:
    working_hours:
      # don't turn on when 'Off' mode is selected
      - x: "Break() if state('input_select.purifier_salon_mode') == 'Off' else Skip()"
      # don't turn on when 'Manual' mode is selected
      - x: "Break() if state('input_select.purifier_salon_mode') == 'Manual' else Skip()"
      # don't turn on when 'away' mode is selected
      - x: "Break() if is_on('input_boolean.away') else Skip()"
      # on weekends and holidays
      - rules:
        - x: "Skip() if is_on('input_boolean.holidays') else Break()"
          weekdays: "!6-7"
        - { start: "00:00", end: "00:00" }

    working_hours_salon:
      - x: "IncludeSchedule(schedule_snippets['working_hours'])"
      - { weekdays: 1-5, start: "14:30", end: "07:00" }

  watched_entities:
    - "input_boolean.away"

  rooms:
    salon:
      friendly_name: Purifier salon
      actors:
        fan.xiaomi_air_purifier_salon:
      
      schedule:
        - v: ('on', 'Silent')
          rules:
            - x: "Break() if state('input_select.purifier_' + room_name + '_mode') != 'Silent' else Skip()"
            - x: "IncludeSchedule(schedule_snippets['working_hours_salon'])"

        - v: ('on', 'Auto')
          rules:
            - x: "Break() if state('input_select.purifier_' + room_name + '_mode') != 'Auto' else Skip()"
            - x: "IncludeSchedule(schedule_snippets['working_hours_salon'])"

        - v: ('on', 'Favorite')
          rules:
            - x: "Break() if state('input_select.purifier_' + room_name + '_mode') != 'Favorite' else Skip()"
            - x: "IncludeSchedule(schedule_snippets['working_hours_salon'])"

            # don't turn on when pm2.5 < 10
            - x: "Break() if float(state('sensor.salon_pm25') or 0.0) < 10.0 else Skip()"

        - x: "'off' if state('input_select.purifier_salon_mode') != 'Manual' or is_on('input_boolean.away') else Skip()"

      watched_entities:
        - "sensor.salon_pm25"
        - "input_select.purifier_mode"
        - "input_boolean.holidays"

How can I pass extra params to actor service?

BR,
Seba

Sorry, the docs were wrong in that regard. It is value_parameter, not value_param.

That’s ok,I figured it out by looking at source code ;-).

I think I finally understand how it works. It was not obvious for me but correct me if I am wrong:
Scheduler has to return a tuple with as many arguments as number of defined attributes.

To make it working for me I had to define “dummy” value for each attribute and pass it with result if it is not needed for given configuration.

From my point of view it would be great to have possibility to define something like “no action value” for each attribute. Maybe such value can be built-in into Schedy like “_other _” used for wildcard.

@roschi, what do you think about that?

Hmm, that’s difficult. The values don’t represent actions, they represent states, and it must be possible to unambiguously map an actor’s (entity’s) state to a value and vice-versa for the algorithms to work correctly.

You could implement an own type of actor that maps value -> state -> value differently, but the generic actor can’t.

EDIT: Or, to say it different, the generic actor can’t ignore the value of a slot depending on another slot’s value.

I understand. I’ve looked into the code and it seems that Schedy will send actions/call services in order taken from configuration.
That’s ok, I can play with attributes order and try to make a little trick by setting up some attribute with same values. Is it possible to do something like that in scheduler:

  • x: [‘auto’, ‘on’, float(state(‘input_number.purifier_salon_favorite_level’) or 0.0).str()]

with such approach i won’t change value of attribute.

EDIT: What I want to achieve is to have possibility for changing some subsets of attributes

Of what type should the third value be, float or str? If it’s read from an entity’s attribute other than state, it should probably be float.

It has to be integer value passed as string
EDIT: I’ve never used Python so please be gentle

Then str(state(...) or 0).

EDIT: What I want to achieve is to have possibility for changing some subsets of attributes

But be aware that this isn’t safe to use and can cause a race condition when schedules are evaluated before the entities are fully loaded, because state() or 0) will then result in 0 and do change the value permanently.

You should guard that rule somehow, possibly with a Break() or even Abort() when the fan entity isn’t loaded yet. You can then even omit the or 0.

I’ll take care about race conditions when everything else will be working :slight_smile:
I believe I’m close to achieve what I want. Last thing is to figure out how to call device specific service from Schedy. It is not working now…

I believe I’m close to achieve what I want. Last thing is to figure out how to call device specific service from Schedy. It is not working now…

Don’t know what a device-specific service is.

Don’t know what a device-specific service is.

This is service from xiaomi purifier used for setting up fan level (fan.xiaomi_miio_set_favorite_level)
I can call this service from dev-service page of HA but when putting this into Schedy I’ve got such error:

Requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: http://myhass:8123/api/services/fan/xiaomi_miio_set_favorite_level

Hmmm, that sounds weird… What does the actor config look like?

  actor_type: generic
  actor_templates:
    default:
      attributes:
        - attribute: 'mode'
          values:
            'auto':
              service: "fan/set_speed"
              service_data: {}
              value_parameter: 'speed'
            'silent':
              service: "fan/set_speed"
              service_data: {}
              value_parameter: 'speed'
            'favorite':
              service: "fan/set_speed"
              service_data: {}
              value_parameter: 'speed'
        - attribute: "favorite_level"
          values:
            '_other_':
              service: "fan/xiaomi_miio_set_favorite_level"
              value_parameter: 'level'
        - attribute: 'state'
          values:
            'off':
              service: "fan/turn_off"
            'on':
              service: "fan/turn_on"

Hm, looks correct to me.

Maybe you try writing a really minimal example without Schedy and just plain AppDaemon to demonstrate this and then ask @ReneTode, as that seems to be an issue with AppDaemon.