xref: /openbmc/phosphor-fan-presence/control/gen-fan-zone-defs.py (revision 9a5b6994c745480a78ef658148cfd96dd3e12bdb)
1#!/usr/bin/env python
2
3"""
4This script reads in fan definition and zone definition YAML
5files and generates a set of structures for use by the fan control code.
6"""
7
8import os
9import sys
10import yaml
11from argparse import ArgumentParser
12from mako.template import Template
13
14tmpl = '''\
15<%!
16def indent(str, depth):
17    return ''.join(4*' '*depth+line for line in str.splitlines(True))
18%>\
19<%def name="genHandler(sig)" buffered="True">
20%if ('type' in sig['sparams']) and \
21    (sig['sparams']['type'] is not None):
22${sig['signal']}<${sig['sparams']['type']}>(
23%else:
24${sig['signal']}(
25%endif
26%for spk in sig['sparams']['params']:
27${sig['sparams'][spk]},
28%endfor
29%if ('type' in sig['hparams']) and \
30    (sig['hparams']['type'] is not None):
31handler::${sig['handler']}<${sig['hparams']['type']}>(
32%else:
33handler::${sig['handler']}(
34%endif
35%for i, hpk in enumerate(sig['hparams']['params']):
36    %if (i+1) != len(sig['hparams']['params']):
37    ${sig['hparams'][hpk]},
38    %else:
39    ${sig['hparams'][hpk]}
40    %endif
41%endfor
42))
43</%def>\
44<%def name="genSSE(event)" buffered="True">
45Group{
46%for member in event['group']:
47{
48    "${member['object']}",
49    {"${member['interface']}",
50     "${member['property']}"}
51},
52%endfor
53},
54std::vector<Action>{
55%for a in event['action']:
56%if len(a['parameters']) != 0:
57make_action(action::${a['name']}(
58%else:
59make_action(action::${a['name']}
60%endif
61%for i, p in enumerate(a['parameters']):
62%if (i+1) != len(a['parameters']):
63    ${p},
64%else:
65    ${p})
66%endif
67%endfor
68),
69%endfor
70},
71Timer{
72    ${event['timer']['interval']},
73    ${event['timer']['type']}
74},
75std::vector<Signal>{
76%for s in event['signals']:
77    Signal{
78        match::${s['match']}(
79        %for i, mp in enumerate(s['mparams']):
80        %if (i+1) != len(s['mparams']):
81        "${mp}",
82        %else:
83        "${mp}"
84        %endif
85        %endfor
86        ),
87        make_handler(\
88        ${indent(genHandler(sig=s), 3)}\
89        )
90    },
91%endfor
92}
93</%def>\
94/* This is a generated file. */
95#include "manager.hpp"
96#include "functor.hpp"
97#include "actions.hpp"
98#include "handlers.hpp"
99#include "preconditions.hpp"
100#include "matches.hpp"
101
102using namespace phosphor::fan::control;
103
104const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}};
105
106const std::vector<ZoneGroup> Manager::_zoneLayouts
107{
108%for zone_group in zones:
109    ZoneGroup{
110        std::vector<Condition>{
111        %for condition in zone_group['conditions']:
112            Condition{
113                "${condition['type']}",
114                std::vector<ConditionProperty>{
115                %for property in condition['properties']:
116                    ConditionProperty{
117                        "${property['property']}",
118                        "${property['interface']}",
119                        "${property['path']}",
120                        static_cast<${property['type']}>(${property['value']}),
121                    },
122                    %endfor
123                },
124            },
125            %endfor
126        },
127        std::vector<ZoneDefinition>{
128        %for zone in zone_group['zones']:
129            ZoneDefinition{
130                ${zone['num']},
131                ${zone['full_speed']},
132                ${zone['default_floor']},
133                ${zone['increase_delay']},
134                ${zone['decrease_interval']},
135                std::vector<FanDefinition>{
136                %for fan in zone['fans']:
137                    FanDefinition{
138                        "${fan['name']}",
139                        std::vector<std::string>{
140                        %for sensor in fan['sensors']:
141                            "${sensor}",
142                        %endfor
143                        }
144                    },
145                %endfor
146                },
147                std::vector<SetSpeedEvent>{
148                %for event in zone['events']:
149                    %if ('pc' in event) and \
150                        (event['pc'] is not None):
151                    SetSpeedEvent{
152                        Group{
153                        %for member in event['pc']['pcgrp']:
154                        {
155                            "${member['object']}",
156                            {"${member['interface']}",
157                             "${member['property']}"}
158                        },
159                        %endfor
160                        },
161                        std::vector<Action>{
162                        %for i, a in enumerate(event['pc']['pcact']):
163                        %if len(a['params']) != 0:
164                        make_action(
165                            precondition::${a['name']}(
166                        %else:
167                        make_action(
168                            precondition::${a['name']}
169                        %endif
170                        %for p in a['params']:
171                        ${p['type']}${p['open']}
172                        %for j, v in enumerate(p['values']):
173                        %if (j+1) != len(p['values']):
174                            ${v['value']},
175                        %else:
176                            ${v['value']}
177                        %endif
178                        %endfor
179                        ${p['close']},
180                        %endfor
181                        %if (i+1) != len(event['pc']['pcact']):
182                        %if len(a['params']) != 0:
183                        )),
184                        %else:
185                        ),
186                        %endif
187                        %endif
188                        %endfor
189                    std::vector<SetSpeedEvent>{
190                    %for pcevt in event['pc']['pcevts']:
191                    SetSpeedEvent{\
192                    ${indent(genSSE(event=pcevt), 6)}\
193                    },
194                    %endfor
195                    %else:
196                    SetSpeedEvent{\
197                    ${indent(genSSE(event=event), 6)}
198                    %endif
199                    %if ('pc' in event) and (event['pc'] is not None):
200                    }
201                        %if len(event['pc']['pcact'][-1]['params']) != 0:
202                        )),
203                        %else:
204                        ),
205                        %endif
206                        },
207                        Timer{
208                            ${event['pc']['pctime']['interval']},
209                            ${event['pc']['pctime']['type']}
210                        },
211                        std::vector<Signal>{
212                        %for s in event['pc']['pcsigs']:
213                            Signal{
214                                match::${s['match']}(
215                                %for i, mp in enumerate(s['mparams']):
216                                %if (i+1) != len(s['mparams']):
217                                "${mp}",
218                                %else:
219                                "${mp}"
220                                %endif
221                                %endfor
222                                ),
223                                make_handler(\
224                                ${indent(genHandler(sig=s), 9)}\
225                                )
226                            },
227                        %endfor
228                        }
229                    %endif
230                    },
231                %endfor
232                }
233            },
234        %endfor
235        }
236    },
237%endfor
238};
239'''
240
241
242def convertToMap(listOfDict):
243    """
244    Converts a list of dictionary entries to a std::map initialization list.
245    """
246    listOfDict = listOfDict.replace('\'', '\"')
247    listOfDict = listOfDict.replace('[', '{')
248    listOfDict = listOfDict.replace(']', '}')
249    listOfDict = listOfDict.replace(':', ',')
250    return listOfDict
251
252
253def getActions(edata, actions, events):
254    """
255    Extracts and constructs the make_action function call for
256    all the actions within the given event.
257    """
258    action = []
259    for eActions in actions['actions']:
260        actions = {}
261        eAction = next(a for a in events['actions']
262                       if a['name'] == eActions['name'])
263        actions['name'] = eAction['name']
264        params = []
265        if ('parameters' in eAction) and \
266           (eAction['parameters'] is not None):
267            for p in eAction['parameters']:
268                param = "static_cast<"
269                if type(eActions[p]) is not dict:
270                    if p == 'actions':
271                        param = "std::vector<Action>{"
272                        pActs = getActions(edata, eActions, events)
273                        for a in pActs:
274                            if (len(a['parameters']) != 0):
275                                param += (
276                                    "make_action(action::" +
277                                    a['name'] +
278                                    "(\n")
279                                for i, ap in enumerate(a['parameters']):
280                                    if (i+1) != len(a['parameters']):
281                                        param += (ap + ",")
282                                    else:
283                                        param += (ap + ")")
284                            else:
285                                param += ("make_action(action::" + a['name'])
286                            param += "),"
287                        param += "}"
288                    elif p == 'property':
289                        if isinstance(eActions[p], str) or \
290                           "string" in str(edata['property']['type']).lower():
291                            param += (
292                                str(edata['property']['type']).lower() +
293                                ">(\"" + str(eActions[p]) + "\")")
294                        else:
295                            param += (
296                                str(edata['property']['type']).lower() +
297                                ">(" + str(eActions[p]).lower() + ")")
298                    else:
299                        # Default type to 'size_t' when not given
300                        param += ("size_t>(" + str(eActions[p]).lower() + ")")
301                else:
302                    if p == 'timer':
303                        param = (
304                            "Timer{static_cast<std::chrono::seconds>(" +
305                            str(eActions[p]['delay']) + "), " +
306                            "util::Timer::TimerType::" +
307                            str(eActions[p]['type']) + "}")
308                    else:
309                        param += (str(eActions[p]['type']).lower() + ">(")
310                        if p != 'map':
311                            if isinstance(eActions[p]['value'], str) or \
312                               "string" in str(eActions[p]['type']).lower():
313                                param += \
314                                    "\"" + str(eActions[p]['value']) + "\")"
315                            else:
316                                param += \
317                                    str(eActions[p]['value']).lower() + ")"
318                        else:
319                            param += (
320                                str(eActions[p]['type']).lower() +
321                                convertToMap(str(eActions[p]['value'])) + ")")
322                params.append(param)
323        actions['parameters'] = params
324        action.append(actions)
325    return action
326
327
328def getEvent(zone_num, zone_conditions, e, events_data):
329    """
330    Parses the sections of an event and populates the properties
331    that construct an event within the generated source.
332    """
333    event = {}
334    # Zone numbers are optional in the events yaml but skip if this
335    # zone's zone number is not in the event's zone numbers
336    if all('zones' in z and
337           z['zones'] is not None and
338           zone_num not in z['zones']
339           for z in e['zone_conditions']):
340        return
341
342    # Zone conditions are optional in the events yaml but skip
343    # if this event's condition is not in this zone's conditions
344    if all('name' in z and z['name'] is not None and
345           not any(c['name'] == z['name'] for c in zone_conditions)
346           for z in e['zone_conditions']):
347        return
348
349    # Add set speed event group
350    group = []
351    groups = next(g for g in events_data['groups']
352                  if g['name'] == e['group'])
353    for member in groups['members']:
354        members = {}
355        members['object'] = (groups['type'] +
356                             member)
357        members['interface'] = e['interface']
358        members['property'] = e['property']['name']
359        members['type'] = e['property']['type']
360        group.append(members)
361    event['group'] = group
362
363    # Add set speed actions and function parameters
364    event['action'] = getActions(e, e, events_data)
365
366    # Add signal handlers
367    signals = []
368    for member in group:
369        for eMatches in e['matches']:
370            signal = {}
371            eMatch = next(m for m in events_data['matches']
372                          if m['name'] == eMatches['name'])
373            signal['match'] = eMatch['name']
374            params = []
375            if ('parameters' in eMatch) and \
376               (eMatch['parameters'] is not None):
377                for p in eMatch['parameters']:
378                    params.append(member[str(p)])
379            signal['mparams'] = params
380            eSignal = next(s for s in events_data['signals']
381                           if s['name'] == eMatch['signal'])
382            signal['signal'] = eSignal['name']
383            sparams = {}
384            if ('parameters' in eSignal) and \
385               (eSignal['parameters'] is not None):
386                splist = []
387                for p in eSignal['parameters']:
388                    sp = str(p)
389                    if (sp != 'type'):
390                        splist.append(sp)
391                        if (sp != 'group'):
392                            sparams[sp] = "\"" + member[sp] + "\""
393                        else:
394                            sparams[sp] = "Group{\n"
395                            for m in group:
396                                sparams[sp] += (
397                                    "{\n" +
398                                    "\"" + str(m['object']) + "\",\n" +
399                                    "{\"" + str(m['interface']) + "\"," +
400                                    "\"" + str(m['property']) + "\"}\n" +
401                                    "},\n")
402                            sparams[sp] += "}"
403                    else:
404                        sparams[sp] = member[sp]
405                sparams['params'] = splist
406            signal['sparams'] = sparams
407            # Add signal handler
408            eHandler = next(h for h in events_data['handlers']
409                            if h['name'] == eSignal['handler'])
410            signal['handler'] = eHandler['name']
411            hparams = {}
412            if ('parameters' in eHandler) and \
413               (eHandler['parameters'] is not None):
414                hplist = []
415                for p in eHandler['parameters']:
416                    hp = str(p)
417                    if (hp != 'type'):
418                        hplist.append(hp)
419                        if (hp != 'group'):
420                            hparams[hp] = "\"" + member[hp] + "\""
421                        else:
422                            hparams[hp] = "Group{\n"
423                            for m in group:
424                                hparams[hp] += (
425                                    "{\n" +
426                                    "\"" + str(m['object']) + "\",\n" +
427                                    "{\"" + str(m['interface']) + "\"," +
428                                    "\"" + str(m['property']) + "\"}\n" +
429                                    "},\n")
430                            hparams[hp] += "}"
431                    else:
432                        hparams[hp] = member[hp]
433                hparams['params'] = hplist
434            signal['hparams'] = hparams
435            signals.append(signal)
436    event['signals'] = signals
437
438    # Add optional action call timer
439    timer = {}
440    interval = "static_cast<std::chrono::seconds>"
441    if ('timer' in e) and \
442       (e['timer'] is not None):
443        timer['interval'] = (interval +
444                             "(" +
445                             str(e['timer']['interval']) +
446                             ")")
447    else:
448        timer['interval'] = (interval +
449                             "(" + str(0) + ")")
450    timer['type'] = "util::Timer::TimerType::repeating"
451    event['timer'] = timer
452
453    return event
454
455
456def addPrecondition(zNum, zCond, event, events_data):
457    """
458    Parses the precondition section of an event and populates the necessary
459    structures to generate a precondition for a set speed event.
460    """
461    precond = {}
462    # Add set speed event precondition group
463    group = []
464    for grp in event['precondition']['groups']:
465        groups = next(g for g in events_data['groups']
466                      if g['name'] == grp['name'])
467        for member in groups['members']:
468            members = {}
469            members['object'] = (groups['type'] +
470                                 member)
471            members['interface'] = grp['interface']
472            members['property'] = grp['property']['name']
473            members['type'] = grp['property']['type']
474            if isinstance(grp['property']['value'], str) or \
475               "string" in str(members['type']).lower():
476                members['value'] = "\"" + grp['property']['value'] + "\""
477            else:
478                members['value'] = grp['property']['value']
479            group.append(members)
480    precond['pcgrp'] = group
481
482    # Add set speed event precondition actions
483    pc = []
484    pcs = {}
485    pcs['name'] = event['precondition']['name']
486    epc = next(p for p in events_data['preconditions']
487               if p['name'] == event['precondition']['name'])
488    params = []
489    for p in epc['parameters']:
490        param = {}
491        if p == 'groups':
492            param['type'] = "std::vector<PrecondGroup>"
493            param['open'] = "{"
494            param['close'] = "}"
495            values = []
496            for pcgrp in group:
497                value = {}
498                value['value'] = (
499                    "PrecondGroup{\"" +
500                    str(pcgrp['object']) + "\",\"" +
501                    str(pcgrp['interface']) + "\",\"" +
502                    str(pcgrp['property']) + "\"," +
503                    "static_cast<" +
504                    str(pcgrp['type']).lower() + ">")
505                if isinstance(pcgrp['value'], str) or \
506                   "string" in str(pcgrp['type']).lower():
507                    value['value'] += ("(" + str(pcgrp['value']) + ")}")
508                else:
509                    value['value'] += \
510                        ("(" + str(pcgrp['value']).lower() + ")}")
511                values.append(value)
512            param['values'] = values
513        params.append(param)
514    pcs['params'] = params
515    pc.append(pcs)
516    precond['pcact'] = pc
517
518    pcevents = []
519    for pce in event['precondition']['events']:
520        pcevent = getEvent(zNum, zCond, pce, events_data)
521        if not pcevent:
522            continue
523        pcevents.append(pcevent)
524    precond['pcevts'] = pcevents
525
526    # Add precondition signal handlers
527    signals = []
528    for member in group:
529        for eMatches in event['precondition']['matches']:
530            signal = {}
531            eMatch = next(m for m in events_data['matches']
532                          if m['name'] == eMatches['name'])
533            signal['match'] = eMatch['name']
534            params = []
535            if ('parameters' in eMatch) and \
536               (eMatch['parameters'] is not None):
537                for p in eMatch['parameters']:
538                    params.append(member[str(p)])
539            signal['mparams'] = params
540            eSignal = next(s for s in events_data['signals']
541                           if s['name'] == eMatch['signal'])
542            signal['signal'] = eSignal['name']
543            sparams = {}
544            if ('parameters' in eSignal) and \
545               (eSignal['parameters'] is not None):
546                splist = []
547                for p in eSignal['parameters']:
548                    sp = str(p)
549                    if (sp != 'type'):
550                        splist.append(sp)
551                        if (sp != 'group'):
552                            sparams[sp] = "\"" + member[sp] + "\""
553                        else:
554                            sparams[sp] = "Group{\n"
555                            for m in group:
556                                sparams[sp] += (
557                                    "{\n" +
558                                    "\"" + str(m['object']) + "\",\n" +
559                                    "{\"" + str(m['interface']) + "\"," +
560                                    "\"" + str(m['property']) + "\"}\n" +
561                                    "},\n")
562                            sparams[sp] += "}"
563                    else:
564                        sparams[sp] = member[sp]
565                sparams['params'] = splist
566            signal['sparams'] = sparams
567            # Add signal handler
568            eHandler = next(h for h in events_data['handlers']
569                            if h['name'] == eSignal['handler'])
570            signal['handler'] = eHandler['name']
571            hparams = {}
572            if ('parameters' in eHandler) and \
573               (eHandler['parameters'] is not None):
574                hplist = []
575                for p in eHandler['parameters']:
576                    hp = str(p)
577                    if (hp != 'type'):
578                        hplist.append(hp)
579                        if (hp != 'group'):
580                            hparams[hp] = "\"" + member[hp] + "\""
581                        else:
582                            hparams[hp] = "Group{\n"
583                            for m in group:
584                                hparams[hp] += (
585                                    "{\n" +
586                                    "\"" + str(m['object']) + "\",\n" +
587                                    "{\"" + str(m['interface']) + "\"," +
588                                    "\"" + str(m['property']) + "\"}\n" +
589                                    "},\n")
590                            hparams[hp] += "}"
591                    else:
592                        hparams[hp] = member[hp]
593                hparams['params'] = hplist
594            signal['hparams'] = hparams
595            signals.append(signal)
596    precond['pcsigs'] = signals
597
598    # Add optional action call timer
599    timer = {}
600    interval = "static_cast<std::chrono::seconds>"
601    if ('timer' in event['precondition']) and \
602       (event['precondition']['timer'] is not None):
603        timer['interval'] = (interval +
604                             "(" +
605                             str(event['precondition']['timer']['interval']) +
606                             ")")
607    else:
608        timer['interval'] = (interval +
609                             "(" + str(0) + ")")
610    timer['type'] = "util::Timer::TimerType::repeating"
611    precond['pctime'] = timer
612
613    return precond
614
615
616def getEventsInZone(zone_num, zone_conditions, events_data):
617    """
618    Constructs the event entries defined for each zone using the events yaml
619    provided.
620    """
621    events = []
622
623    if 'events' in events_data:
624        for e in events_data['events']:
625            event = {}
626            # Add precondition if given
627            if ('precondition' in e) and \
628               (e['precondition'] is not None):
629                event['pc'] = addPrecondition(zone_num,
630                                              zone_conditions,
631                                              e,
632                                              events_data)
633            else:
634                event = getEvent(zone_num, zone_conditions, e, events_data)
635                if not event:
636                    continue
637            events.append(event)
638
639    return events
640
641
642def getFansInZone(zone_num, profiles, fan_data):
643    """
644    Parses the fan definition YAML files to find the fans
645    that match both the zone passed in and one of the
646    cooling profiles.
647    """
648
649    fans = []
650
651    for f in fan_data['fans']:
652
653        if zone_num != f['cooling_zone']:
654            continue
655
656        # 'cooling_profile' is optional (use 'all' instead)
657        if f.get('cooling_profile') is None:
658            profile = "all"
659        else:
660            profile = f['cooling_profile']
661
662        if profile not in profiles:
663            continue
664
665        fan = {}
666        fan['name'] = f['inventory']
667        fan['sensors'] = f['sensors']
668        fans.append(fan)
669
670    return fans
671
672
673def getConditionInZoneConditions(zone_condition, zone_conditions_data):
674    """
675    Parses the zone conditions definition YAML files to find the condition
676    that match both the zone condition passed in.
677    """
678
679    condition = {}
680
681    for c in zone_conditions_data['conditions']:
682
683        if zone_condition != c['name']:
684            continue
685        condition['type'] = c['type']
686        properties = []
687        for p in c['properties']:
688            property = {}
689            property['property'] = p['property']
690            property['interface'] = p['interface']
691            property['path'] = p['path']
692            property['type'] = p['type'].lower()
693            property['value'] = str(p['value']).lower()
694            properties.append(property)
695        condition['properties'] = properties
696
697        return condition
698
699
700def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
701    """
702    Combines the zone definition YAML and fan
703    definition YAML to create a data structure defining
704    the fan cooling zones.
705    """
706
707    zone_groups = []
708
709    for group in zone_data:
710        conditions = []
711        # zone conditions are optional
712        if 'zone_conditions' in group and group['zone_conditions'] is not None:
713            for c in group['zone_conditions']:
714
715                if not zone_conditions_data:
716                    sys.exit("No zone_conditions YAML file but " +
717                             "zone_conditions used in zone YAML")
718
719                condition = getConditionInZoneConditions(c['name'],
720                                                         zone_conditions_data)
721
722                if not condition:
723                    sys.exit("Missing zone condition " + c['name'])
724
725                conditions.append(condition)
726
727        zone_group = {}
728        zone_group['conditions'] = conditions
729
730        zones = []
731        for z in group['zones']:
732            zone = {}
733
734            # 'zone' is required
735            if ('zone' not in z) or (z['zone'] is None):
736                sys.exit("Missing fan zone number in " + zone_yaml)
737
738            zone['num'] = z['zone']
739
740            zone['full_speed'] = z['full_speed']
741
742            zone['default_floor'] = z['default_floor']
743
744            # 'increase_delay' is optional (use 0 by default)
745            key = 'increase_delay'
746            zone[key] = z.setdefault(key, 0)
747
748            # 'decrease_interval' is optional (use 0 by default)
749            key = 'decrease_interval'
750            zone[key] = z.setdefault(key, 0)
751
752            # 'cooling_profiles' is optional (use 'all' instead)
753            if ('cooling_profiles' not in z) or \
754                    (z['cooling_profiles'] is None):
755                profiles = ["all"]
756            else:
757                profiles = z['cooling_profiles']
758
759            fans = getFansInZone(z['zone'], profiles, fan_data)
760            events = getEventsInZone(z['zone'], group['zone_conditions'],
761                                     events_data)
762
763            if len(fans) == 0:
764                sys.exit("Didn't find any fans in zone " + str(zone['num']))
765
766            zone['fans'] = fans
767            zone['events'] = events
768            zones.append(zone)
769
770        zone_group['zones'] = zones
771        zone_groups.append(zone_group)
772
773    return zone_groups
774
775
776if __name__ == '__main__':
777    parser = ArgumentParser(
778        description="Phosphor fan zone definition parser")
779
780    parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
781                        default="example/zones.yaml",
782                        help='fan zone definitional yaml')
783    parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
784                        default="example/fans.yaml",
785                        help='fan definitional yaml')
786    parser.add_argument('-e', '--events_yaml', dest='events_yaml',
787                        help='events to set speeds yaml')
788    parser.add_argument('-c', '--zone_conditions_yaml',
789                        dest='zone_conditions_yaml',
790                        help='conditions to determine zone yaml')
791    parser.add_argument('-o', '--output_dir', dest='output_dir',
792                        default=".",
793                        help='output directory')
794    args = parser.parse_args()
795
796    if not args.zone_yaml or not args.fan_yaml:
797        parser.print_usage()
798        sys.exit(-1)
799
800    with open(args.zone_yaml, 'r') as zone_input:
801        zone_data = yaml.safe_load(zone_input) or {}
802
803    with open(args.fan_yaml, 'r') as fan_input:
804        fan_data = yaml.safe_load(fan_input) or {}
805
806    events_data = {}
807    if args.events_yaml:
808        with open(args.events_yaml, 'r') as events_input:
809            events_data = yaml.safe_load(events_input) or {}
810
811    zone_conditions_data = {}
812    if args.zone_conditions_yaml:
813        with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
814            zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
815
816    zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
817                                fan_data, events_data, zone_conditions_data)
818
819    manager_config = zone_data.get('manager_configuration', {})
820
821    if manager_config.get('power_on_delay') is None:
822        manager_config['power_on_delay'] = 0
823
824    output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
825    with open(output_file, 'w') as output:
826        output.write(Template(tmpl).render(zones=zone_config,
827                     mgr_data=manager_config))
828