devices/CompositeDevice.js

const Device = require ('./Device.js').Device;
const inherit = require('../tools.js').inherit;

/**
 *  Represents a device composed of multiple devices like simple HATs, H-bridge motor controllers, robots composed of multiple motors, etc.
 *  The constructor accepts subordinate devices as positional or keyword arguments.
 *  Positional arguments form unnamed devices accessed via the (@link CompositeDevice#all|all) attribute,
 *  while keyword arguments are added to the device as named attributes.
 *
 * @param {Array} devices - An Array of positional devices.
 * @param {Array} kwdevices - An Array of tuples containing device name and device.
 * @class
 * @augments Device
 */
function CompositeDevice(devices, kwdevices) {
    this._all = [];
    this._namedtuple = [];
    let i;
    if(devices !== undefined) {
        for (i = 0; i < devices.length; i++) {
            this[i] = devices[i];
            this._namedtuple.push('_' + i);
            this._all.push(devices[i]);
        }
    }

    if(kwdevices !== undefined) {
        for (i = 0; i < kwdevices.length; i++) {
            const device_name = kwdevices[i][0];
            this[device_name] = kwdevices[i][1];
            this._namedtuple.push(kwdevices[i][0]);
            this._all.push(kwdevices[i][1]);
        }
    }
    Device.call(this);
}

CompositeDevice.prototype = inherit(Device.prototype);
CompositeDevice.prototype.constructor = CompositeDevice;

/**
 *
 * @returns {number} - The number of subordinate devices.
 */
CompositeDevice.prototype.length = function() {
    return this._all.length;
};

/**
 *
 * @returns {Array} - An array of subordinate device names.
 */
CompositeDevice.prototype.namedtuple = function() {
    return this._namedtuple;
};

/**
 *
 * @returns {Array} - An array of all subordinate device values.
 */
CompositeDevice.prototype.value = function () {
    let i;
    const result = [];
    for (i = 0; i < this._all.length; i++) {
        result[i] = this._all[i].value();
    }
    return result;
};

/**
 *
 * @returns {boolean} - An array of each subordinate devices active state.
 */
CompositeDevice.prototype.is_active = function () {
    let i;
    for (i = 0; i < this._all.length; i++) {
        if (this._all[i].value()) {
            return true;
        }
    }
    return false;
};

/**
 * Close all subordinate devices.
 */
CompositeDevice.prototype.close = function () {
    this._all.forEach((device) => {
        device.close();
    });
};

exports.CompositeDevice = CompositeDevice;
/*
 class CompositeDevice(Device):

 :param list _order:
 If specified, this is the order of named items specified by keyword
 arguments (to ensure that the :attr:`value` tuple is constructed with a
 specific order). All keyword arguments *must* be included in the
 collection. If omitted, an alphabetically sorted order will be selected
 for keyword arguments.
 """
 def __init__(self, *args, **kwargs):
 self._all = ()
 self._named = frozendict({})
 self._namedtuple = None
 self._order = kwargs.pop('_order', None)
 if self._order is None:
 self._order = sorted(kwargs.keys())
 else:
 for missing_name in set(kwargs.keys()) - set(self._order):
 raise CompositeDeviceBadOrder('%s missing from _order' % missing_name)
 self._order = tuple(self._order)
 super(CompositeDevice, self).__init__()
 for name in set(self._order) & set(dir(self)):
 raise CompositeDeviceBadName('%s is a reserved name' % name)
 self._all = args + tuple(kwargs[v] for v in self._order)
 for dev in self._all:
 if not isinstance(dev, Device):
 raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev)
 self._named = frozendict(kwargs)
 self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain(
 (str(i) for i in range(len(args))), self._order),
 rename=True)

 def __getattr__(self, name):
 # if _named doesn't exist yet, pretend it's an empty dict
 if name == '_named':
 return frozendict({})
 try:
 return self._named[name]
 except KeyError:
 raise AttributeError("no such attribute %s" % name)

 def __setattr__(self, name, value):
 # make named components read-only properties
 if name in self._named:
 raise AttributeError("can't set attribute %s" % name)
 return super(CompositeDevice, self).__setattr__(name, value)

 def __repr__(self):
 try:
 self._check_open()
 return "<gpiozero.%s object containing %d devices: %s and %d unnamed>" % (
 self.__class__.__name__,
 len(self), ','.join(self._order),
 len(self) - len(self._named)
 )
 except DeviceClosed:
 return "<gpiozero.%s object closed>" % (self.__class__.__name__)

 def __getitem__(self, index):
 return self._all[index]

 def __iter__(self):
 return iter(self._all)

 @property
 def all(self):
 # XXX Deprecate this in favour of using the instance as a container
 return self._all

 def close(self):
 if self._all:
 for device in self._all:
 device.close()

 @property
 def closed(self):
 return all(device.closed for device in self)

 @property
 def namedtuple(self):
 return self._namedtuple

 @property
 def value(self):
 return self.namedtuple(*(device.value for device in self))

 @property
 def is_active(self):
 return any(self.value)

 */