コード例 #1
0
bool ClimateControlBehaviour::accessField(PropertyAccessMode aMode, ApiValuePtr aPropValue, PropertyDescriptorPtr aPropertyDescriptor)
{
  if (aPropertyDescriptor->hasObjectKey(climatecontrol_key)) {
    if (aMode==access_read) {
      // read properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Description properties
        case activeCoolingMode_key+descriptions_key_offset:
          aPropValue->setBoolValue(climateDeviceKind==climatedevice_fancoilunit); // FCUs can cool actively
          return true;
        // Settings properties
        case heatingSystemCapability_key+settings_key_offset:
          aPropValue->setUint8Value(heatingSystemCapability);
          return true;
        case heatingSystemType_key+settings_key_offset:
          aPropValue->setUint8Value(heatingSystemType);
          return true;
      }
    }
    else {
      // write properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Settings properties
        case heatingSystemCapability_key+settings_key_offset:
          setPVar(heatingSystemCapability, (VdcHeatingSystemCapability)aPropValue->uint8Value());
          return true;
        case heatingSystemType_key+settings_key_offset:
          setPVar(heatingSystemType, (VdcHeatingSystemType)aPropValue->uint8Value());
          return true;
      }
    }
  }
  // not my field, let base class handle it
  return inherited::accessField(aMode, aPropValue, aPropertyDescriptor);
}
コード例 #2
0
ファイル: outputbehaviour.cpp プロジェクト: chhitz/vdcd
PropertyDescriptorPtr OutputBehaviour::getDescriptorByName(string aPropMatch, int &aStartIndex, int aDomain, PropertyDescriptorPtr aParentDescriptor)
{
  if (aParentDescriptor && aParentDescriptor->hasObjectKey(output_groups_key)) {
    // array-like container
    PropertyDescriptorPtr propDesc;
    bool numericName = getNextPropIndex(aPropMatch, aStartIndex);
    int n = numProps(aDomain, aParentDescriptor);
    if (aStartIndex!=PROPINDEX_NONE && aStartIndex<n) {
      // within range, create descriptor
      DynamicPropertyDescriptor *descP = new DynamicPropertyDescriptor(aParentDescriptor);
      descP->propertyName = string_format("%d", aStartIndex);
      descP->propertyType = aParentDescriptor->type();
      descP->propertyFieldKey = aStartIndex;
      descP->propertyObjectKey = aParentDescriptor->objectKey();
      propDesc = PropertyDescriptorPtr(descP);
      // advance index
      aStartIndex++;
    }
    if (aStartIndex>=n || numericName) {
      // no more descriptors OR specific descriptor accessed -> no "next" descriptor
      aStartIndex = PROPINDEX_NONE;
    }
    return propDesc;
  }
  // None of the containers within Device - let base class handle Device-Level properties
  return inherited::getDescriptorByName(aPropMatch, aStartIndex, aDomain, aParentDescriptor);
}
コード例 #3
0
ファイル: outputbehaviour.cpp プロジェクト: chhitz/vdcd
PropertyContainerPtr OutputBehaviour::getContainer(PropertyDescriptorPtr &aPropertyDescriptor, int &aDomain)
{
  if (aPropertyDescriptor->isArrayContainer() && aPropertyDescriptor->hasObjectKey(output_groups_key)) {
    return PropertyContainerPtr(this); // handle groups array myself
  }
  // unknown here
  return inherited::getContainer(aPropertyDescriptor, aDomain);
}
コード例 #4
0
// access to all fields
bool ChannelBehaviour::accessField(PropertyAccessMode aMode, ApiValuePtr aPropValue, PropertyDescriptorPtr aPropertyDescriptor)
{
  if (aPropertyDescriptor->hasObjectKey(channel_Key)) {
    if (aMode==access_read) {
      // read properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Description properties
        case name_key+descriptions_key_offset:
          aPropValue->setStringValue(getName());
          return true;
        case channelIndex_key+descriptions_key_offset:
          aPropValue->setUint8Value(channelIndex);
          return true;
        case min_key+descriptions_key_offset:
          aPropValue->setDoubleValue(getMin());
          return true;
        case max_key+descriptions_key_offset:
          aPropValue->setDoubleValue(getMax());
          return true;
        case resolution_key+descriptions_key_offset:
          aPropValue->setDoubleValue(getResolution());
          return true;
        // Settings properties
        // - none for now
        // States properties
        case value_key+states_key_offset:
          // get value of channel, possibly calculating it if needed (color conversions)
          aPropValue->setDoubleValue(getChannelValueCalculated());
          return true;
        case age_key+states_key_offset:
          if (channelLastSync==Never)
            aPropValue->setNull(); // no value known
          else
            aPropValue->setDoubleValue((double)(MainLoop::now()-channelLastSync)/Second);
          return true;
      }
    }
    else {
      // write properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Settings properties
        // - none for now
        // States properties
        case value_key+states_key_offset:
          setChannelValue(aPropValue->doubleValue(), 0, true); // always apply, no transition time
          return true;
      }
    }
  }
  // single class level properties only, don't call inherited
  return false;
}
コード例 #5
0
ファイル: outputbehaviour.cpp プロジェクト: chhitz/vdcd
int OutputBehaviour::numProps(int aDomain, PropertyDescriptorPtr aParentDescriptor)
{
  if (aParentDescriptor->hasObjectKey(output_groups_key)) {
    return 64; // group mask has 64 bits for now
  }
  return inherited::numProps(aDomain, aParentDescriptor);
}
コード例 #6
0
ファイル: enoceandevice.cpp プロジェクト: chhitz/vdcd
// access to all fields
bool EnoceanDevice::accessField(PropertyAccessMode aMode, ApiValuePtr aPropValue, PropertyDescriptorPtr aPropertyDescriptor)
{
  if (aPropertyDescriptor->hasObjectKey(enoceanDevice_key)) {
    if (aMode==access_read) {
      // read properties
      switch (aPropertyDescriptor->fieldKey()) {
        case profileVariants_key:
          aPropValue->setType(apivalue_object); // make object (incoming object is NULL)
          return getProfileVariants(aPropValue);
        case profile_key:
          aPropValue->setInt32Value(getEEProfile()); return true;
        case packetage_key:
          // Note lastPacketTime is set to now at startup, so additionally check lastRSSI
          if (lastPacketTime==Never || lastRSSI<=-999)
            aPropValue->setNull();
          else
            aPropValue->setDoubleValue((double)(MainLoop::now()-lastPacketTime)/Second);
          return true;
        case rssi_key:
          if (lastRSSI<=-999)
            aPropValue->setNull();
          else
            aPropValue->setInt32Value(lastRSSI);
          return true;
        case repeaterCount_key:
          if (lastRSSI<=-999)
            aPropValue->setNull();
          else
            aPropValue->setUint8Value(lastRepeaterCount);
          return true;
      }
    }
    else {
      // write properties
      switch (aPropertyDescriptor->fieldKey()) {
        case profile_key:
          setProfileVariant(aPropValue->int32Value()); return true;
      }
    }
  }
  // not my field, let base class handle it
  return inherited::accessField(aMode, aPropValue, aPropertyDescriptor);
}
コード例 #7
0
PropertyDescriptorPtr PropertyContainer::getDescriptorByNumericName(
  string aPropMatch, int &aStartIndex, int aDomain, PropertyDescriptorPtr aParentDescriptor,
  intptr_t aObjectKey
)
{
  PropertyDescriptorPtr propDesc;
  getNextPropIndex(aPropMatch, aStartIndex);
  int n = numProps(aDomain, aParentDescriptor);
  if (aStartIndex!=PROPINDEX_NONE && aStartIndex<n) {
    // within range, create descriptor
    DynamicPropertyDescriptor *descP = new DynamicPropertyDescriptor(aParentDescriptor);
    descP->propertyName = string_format("%d", aStartIndex);
    descP->propertyType = aParentDescriptor->type();
    descP->propertyFieldKey = aStartIndex;
    descP->propertyObjectKey = aObjectKey;
    propDesc = PropertyDescriptorPtr(descP);
    // advance index
    aStartIndex++;
  }
  if (aStartIndex>=n)
    aStartIndex = PROPINDEX_NONE;
  return propDesc;
}
コード例 #8
0
bool SensorBehaviour::accessField(PropertyAccessMode aMode, ApiValuePtr aPropValue, PropertyDescriptorPtr aPropertyDescriptor)
{
  if (aPropertyDescriptor->hasObjectKey(sensor_key)) {
    if (aMode==access_read) {
      // read properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Description properties
        case sensorType_key+descriptions_key_offset:
          aPropValue->setUint16Value(sensorType);
          return true;
        case sensorUsage_key+descriptions_key_offset:
          aPropValue->setUint16Value(sensorUsage);
          return true;
        case min_key+descriptions_key_offset:
          aPropValue->setDoubleValue(min);
          return true;
        case max_key+descriptions_key_offset:
          aPropValue->setDoubleValue(max);
          return true;
        case resolution_key+descriptions_key_offset:
          aPropValue->setDoubleValue(resolution);
          return true;
        case updateInterval_key+descriptions_key_offset:
          aPropValue->setDoubleValue((double)updateInterval/Second);
          return true;
        case aliveSignInterval_key+descriptions_key_offset:
          aPropValue->setDoubleValue((double)aliveSignInterval/Second);
          return true;
        // Settings properties
        case group_key+settings_key_offset:
          aPropValue->setUint16Value(sensorGroup);
          return true;
        case minPushInterval_key+settings_key_offset:
          aPropValue->setDoubleValue((double)minPushInterval/Second);
          return true;
        case changesOnlyInterval_key+settings_key_offset:
          aPropValue->setDoubleValue((double)changesOnlyInterval/Second);
          return true;
        // States properties
        case value_key+states_key_offset:
          // value
          if (lastUpdate==Never)
            aPropValue->setNull();
          else
            aPropValue->setDoubleValue(currentValue);
          return true;
        case age_key+states_key_offset:
          // age
          if (lastUpdate==Never)
            aPropValue->setNull();
          else
            aPropValue->setDoubleValue((double)(MainLoop::now()-lastUpdate)/Second);
          return true;
      }
    }
    else {
      // write properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Settings properties
        case group_key+settings_key_offset:
          setPVar(sensorGroup, (DsGroup)aPropValue->int32Value());
          return true;
        case minPushInterval_key+settings_key_offset:
          setPVar(minPushInterval, (MLMicroSeconds)(aPropValue->doubleValue()*Second));
          return true;
        case changesOnlyInterval_key+settings_key_offset:
          setPVar(changesOnlyInterval, (MLMicroSeconds)(aPropValue->doubleValue()*Second));
          return true;
      }
    }
  }
  // not my field, let base class handle it
  return inherited::accessField(aMode, aPropValue, aPropertyDescriptor);
}
コード例 #9
0
ErrorPtr PropertyContainer::accessProperty(PropertyAccessMode aMode, ApiValuePtr aQueryObject, ApiValuePtr aResultObject, int aDomain, PropertyDescriptorPtr aParentDescriptor)
{
  ErrorPtr err;
  #if DEBUGFOCUSLOGGING
  FOCUSLOG("\naccessProperty: entered with query = %s\n", aQueryObject->description().c_str());
  if (aParentDescriptor) {
    FOCUSLOG("- parentDescriptor '%s' (%s), fieldKey=%u, objectKey=%u\n", aParentDescriptor->name(), aParentDescriptor->isStructured() ? "structured" : "scalar", aParentDescriptor->fieldKey(), aParentDescriptor->objectKey());
  }
  #endif
  // for reading, NULL query is like query { "":NULL }
  if (aQueryObject->isNull() && aMode==access_read) {
    aQueryObject->setType(apivalue_object);
    aQueryObject->add("", aQueryObject->newValue(apivalue_null));
  }
  // aApiObject must be of type apivalue_object
  if (!aQueryObject->isType(apivalue_object))
    return ErrorPtr(new VdcApiError(415, "Query or Value written must be object"));
  if (aMode==access_read) {
    if (!aResultObject)
      return ErrorPtr(new VdcApiError(415, "accessing property for read must provide result object"));
    aResultObject->setType(apivalue_object); // must be object
  }
  // Iterate trough elements of query object
  aQueryObject->resetKeyIteration();
  string queryName;
  ApiValuePtr queryValue;
  string errorMsg;
  while (aQueryObject->nextKeyValue(queryName, queryValue)) {
    FOCUSLOG("- starting to process query element named '%s' : %s\n", queryName.c_str(), queryValue->description().c_str());
    if (aMode==access_read && queryName=="#") {
      // asking for number of elements at this level -> generate and return int value
      queryValue = queryValue->newValue(apivalue_int64); // integer
      queryValue->setInt32Value(numProps(aDomain, aParentDescriptor));
      aResultObject->add(queryName, queryValue);
    }
    else {
      // accessing an element or series of elements at this level
      bool wildcard = isMatchAll(queryName);
      // - find all descriptor(s) for this queryName
      PropertyDescriptorPtr propDesc;
      int propIndex = 0;
      bool foundone = false;
      do {
        propDesc = getDescriptorByName(queryName, propIndex, aDomain, aParentDescriptor);
        if (propDesc) {
          foundone = true; // found at least one descriptor for this query element
          FOCUSLOG("  - processing descriptor '%s' (%s), fieldKey=%u, objectKey=%u\n", propDesc->name(), propDesc->isStructured() ? "structured" : "scalar", propDesc->fieldKey(), propDesc->objectKey());
          // actually access by descriptor
          if (propDesc->isStructured()) {
            ApiValuePtr subQuery;
            // property is a container. Now check the value
            if (queryValue->isType(apivalue_object)) {
              subQuery = queryValue; // query specifies next level, just use it
            }
            else if (queryName!="*" && (!wildcard || propDesc->isWildcardAddressable())) {
              // don't recurse deeper when query name is "*" or property is not wildcard-adressable
              // special case is "*" as leaf in query - only recurse if it is not present
              // - autocreate subquery
              subQuery = queryValue->newValue(apivalue_object);
              subQuery->add("", queryValue->newValue(apivalue_null));
            }
            if (subQuery) {
              // addressed property is a container by itself -> recurse
              // - get the PropertyContainer
              int containerDomain = aDomain; // default to same, but getContainer may modify it
              PropertyDescriptorPtr containerPropDesc = propDesc;
              PropertyContainerPtr container = getContainer(containerPropDesc, containerDomain);
              if (container) {
                FOCUSLOG("  - container for '%s' is 0x%p\n", propDesc->name(), container.get());
                FOCUSLOG("    >>>> RECURSING into accessProperty()\n");
                if (aMode==access_read) {
                  // read needs a result object
                  ApiValuePtr resultValue = queryValue->newValue(apivalue_object);
                  err = container->accessProperty(aMode, subQuery, resultValue, containerDomain, containerPropDesc);
                  if (Error::isOK(err)) {
                    // add to result with actual name (from descriptor)
                    FOCUSLOG("\n  <<<< RETURNED from accessProperty() recursion\n");
                    FOCUSLOG("  - accessProperty of container for '%s' returns %s\n", propDesc->name(), resultValue->description().c_str());
                    aResultObject->add(propDesc->name(), resultValue);
                  }
                }
                else {
                  // for write, just pass the query value
                  err = container->accessProperty(aMode, subQuery, ApiValuePtr(), containerDomain, containerPropDesc);
                  FOCUSLOG("    <<<< RETURNED from accessProperty() recursion\n", propDesc->name(), container.get());
                }
                if ((aMode!=access_read) && Error::isOK(err)) {
                  // give this container a chance to post-process write access
                  err = writtenProperty(aMode, propDesc, aDomain, container);
                }
                // 404 errors are collected, but dont abort the query
                if (Error::isError(err, VdcApiError::domain(), 404)) {
                  if (!errorMsg.empty()) errorMsg += "; ";
                  errorMsg += string_format("Error(s) accessing subproperties of '%s' : { %s }", queryName.c_str(), err->description().c_str());
                  err.reset(); // forget the error on this level
                }
              }
            }
          }
          else {
            // addressed (and known by descriptor!) property is a simple value field -> access it
            if (aMode==access_read) {
              // read access: create a new apiValue and have it filled
              ApiValuePtr fieldValue = queryValue->newValue(propDesc->type()); // create a value of correct type to get filled
              bool accessOk = accessField(aMode, fieldValue, propDesc); // read
              // for read, not getting an OK from accessField means: property does not exist (even if known per descriptor),
              // so it will not be added to the result
              if (accessOk) {
                // add to result with actual name (from descriptor)
                aResultObject->add(propDesc->name(), fieldValue);
              }
              FOCUSLOG("    - accessField for '%s' returns %s\n", propDesc->name(), fieldValue->description().c_str());
            }
            else {
              // write access: just pass the value
              if (!accessField(aMode, queryValue, propDesc)) { // write
                err = ErrorPtr(new VdcApiError(403,string_format("Write access to '%s' denied", propDesc->name())));
              }
            }
          }
        }
        else {
          // no descriptor found for this query element
          // Note: this means that property is not KNOWN, which IS not the same as getting false from accessField
          //   (latter means that property IS known, but has no value in the context it was queried)
          //   HOWEVER, for the vdc API it was decided that for reading, these cases should NOT be
          //   distinguished for getProperty. If a property does not exist for either reason, the return tree just does
          //   no contain that property.
          //   Also note that not having a property is not the same as not having a property VALUE:
          //   latter case will explicitly return a NULL value
          if (!wildcard && !foundone && aMode!=access_read) {
            // query did address a specific property but it is unknown -> report as error (for read AND write!)
            // - collect error message, but do not abort query processing
            if (!errorMsg.empty()) errorMsg += "; ";
            errorMsg += string_format("Unknown property '%s' -> ignored", queryName.c_str());
          }
        }
      } while (Error::isOK(err) && propIndex!=PROPINDEX_NONE);
    }
    // now generate error if we have collected a non-empty error message
    if (!errorMsg.empty()) {
      err = ErrorPtr(new VdcApiError(404,errorMsg));
    }
    #if DEBUGLOGGING
    if (aMode==access_read) {
      FOCUSLOG("- query element named '%s' now has result object: %s\n", queryName.c_str(), aResultObject->description().c_str());
    }
    #endif
  }
  return err;
}
コード例 #10
0
// default implementation based on numProps/getDescriptorByIndex
// Derived classes with array-like container may directly override this method for more efficient access
PropertyDescriptorPtr PropertyContainer::getDescriptorByName(string aPropMatch, int &aStartIndex, int aDomain, PropertyDescriptorPtr aParentDescriptor)
{
  int n = numProps(aDomain, aParentDescriptor);
  if (aStartIndex<n && aStartIndex!=PROPINDEX_NONE) {
    // aPropMatch syntax
    // - simple name to match a specific property
    // - empty name or only "*" to match all properties. At this level, there's no difference, but empty causes deep traversal, * does not
    // - name part with a trailing asterisk: wildcard.
    // - #n to access n-th property
    PropertyDescriptorPtr propDesc;
    bool wildcard = false; // assume no wildcard
    if (aPropMatch.empty()) {
      wildcard = true; // implicit wildcard, empty name counts like "*"
    }
    else if (aPropMatch[aPropMatch.size()-1]=='*') {
      wildcard = true; // explicit wildcard at end of string
      aPropMatch.erase(aPropMatch.size()-1); // remove the wildcard char
    }
    else if (aPropMatch[0]=='#') {
      // special case 2 for reading: #n to access n-th subproperty
      int newIndex = n; // set out of range by default
      if (sscanf(aPropMatch.c_str()+1, "%d", &newIndex)==1) {
        // name does not matter, pick item at newIndex unless below current start
        wildcard = true;
        aPropMatch.clear();
        if(newIndex>=aStartIndex)
          aStartIndex = newIndex; // not yet passed this index in iteration -> use it
        else
          aStartIndex = n; // already passed -> make out of range
      }
    }
    while (aStartIndex<n) {
      propDesc = getDescriptorByIndex(aStartIndex, aDomain, aParentDescriptor);
      // check for match
      if (wildcard && aPropMatch.size()==0)
        break; // shortcut for "match all" case
      // match beginning
      if (
        (!wildcard && aPropMatch==propDesc->name()) || // complete match
        (wildcard && (strncmp(aPropMatch.c_str(),propDesc->name(),aPropMatch.size())==0)) // match of name's beginning
      ) {
        break; // this entry matches
      }
      // next
      aStartIndex++;
    }
    if (aStartIndex<n) {
      // found a descriptor
      // - determine next index
      aStartIndex++;
      if (aStartIndex>=n)
        aStartIndex=PROPINDEX_NONE;
      // - return the descriptor
      return propDesc;
    }
  }
  // failure
  // no more descriptors
  aStartIndex=PROPINDEX_NONE;
  return PropertyDescriptorPtr(); // no descriptor
}
コード例 #11
0
ファイル: channelbehaviour.cpp プロジェクト: plan44/p44vdc
// access to all fields
bool ChannelBehaviour::accessField(PropertyAccessMode aMode, ApiValuePtr aPropValue, PropertyDescriptorPtr aPropertyDescriptor)
{
  if (aPropertyDescriptor->hasObjectKey(channel_Key)) {
    if (aMode==access_read) {
      // read properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Description properties
        case name_key+descriptions_key_offset:
          aPropValue->setStringValue(getName());
          return true;
        case channelIndex_key+descriptions_key_offset:
          if (aPropertyDescriptor->getApiVersion()>=3) return false; // property does not exist any more in v3 and later
          aPropValue->setUint8Value(channelIndex);
          return true;
        case dsIndex_key+descriptions_key_offset:
          aPropValue->setUint8Value(channelIndex);
          return true;
        case channelType_key+descriptions_key_offset:
          aPropValue->setUint8Value(getChannelType());
          return true;
        case siunit_key+descriptions_key_offset:
          aPropValue->setStringValue(valueUnitName(getChannelUnit(), false));
          return true;
        case unitsymbol_key+descriptions_key_offset:
          aPropValue->setStringValue(valueUnitName(getChannelUnit(), true));
          return true;
        case min_key+descriptions_key_offset:
          aPropValue->setDoubleValue(getMin());
          return true;
        case max_key+descriptions_key_offset:
          aPropValue->setDoubleValue(getMax());
          return true;
        case resolution_key+descriptions_key_offset:
          aPropValue->setDoubleValue(getResolution());
          return true;
        // Settings properties
        // - none for now
        // States properties
        case value_key+states_key_offset:
          // get value of channel, possibly calculating it if needed (color conversions)
          aPropValue->setDoubleValue(getChannelValueCalculated());
          return true;
        case age_key+states_key_offset:
          if (channelLastSync==Never)
            aPropValue->setNull(); // no value known
          else
            aPropValue->setDoubleValue((double)(MainLoop::now()-channelLastSync)/Second); // time of last sync (does not necessarily relate to currently visible "value", as this might be a to-be-applied new value already)
          return true;
      }
    }
    else {
      // write properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Settings properties
        // - none for now
        // States properties
        case value_key+states_key_offset:
          setChannelValue(aPropValue->doubleValue(), output.transitionTime, true); // always apply, default transition time (normally 0, unless set in outputState)
          return true;
      }
    }
  }
  // single class level properties only, don't call inherited
  return false;
}
コード例 #12
0
ファイル: outputbehaviour.cpp プロジェクト: chhitz/vdcd
bool OutputBehaviour::accessField(PropertyAccessMode aMode, ApiValuePtr aPropValue, PropertyDescriptorPtr aPropertyDescriptor)
{
  if (aPropertyDescriptor->hasObjectKey(output_groups_key)) {
    if (aMode==access_read) {
      // read group membership
      if (isMember((DsGroup)aPropertyDescriptor->fieldKey())) {
        aPropValue->setBoolValue(true);
        return true;
      }
      return false;
    }
    else {
      // write group
      setGroupMembership((DsGroup)aPropertyDescriptor->fieldKey(), aPropValue->boolValue());
      return true;
    }
  }
  else if (aPropertyDescriptor->hasObjectKey(output_key)) {
    if (aMode==access_read) {
      // read properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Description properties
        case outputFunction_key+descriptions_key_offset:
          aPropValue->setUint8Value(outputFunction);
          return true;
        case outputUsage_key+descriptions_key_offset:
          aPropValue->setUint16Value(outputUsage);
          return true;
        case variableRamp_key+descriptions_key_offset:
          aPropValue->setBoolValue(variableRamp);
          return true;
        case maxPower_key+descriptions_key_offset:
          aPropValue->setDoubleValue(maxPower);
          return true;
        // Settings properties
        case mode_key+settings_key_offset:
          aPropValue->setUint8Value(actualOutputMode()); // return actual mode, never outputmode_default
          return true;
        case pushChanges_key+settings_key_offset:
          aPropValue->setBoolValue(pushChanges);
          return true;
        // State properties
        case localPriority_key+states_key_offset:
          aPropValue->setBoolValue(localPriority);
          return true;
      }
    }
    else {
      // write properties
      switch (aPropertyDescriptor->fieldKey()) {
        // Settings properties
        case mode_key+settings_key_offset:
          setOutputMode((DsOutputMode)aPropValue->int32Value());
          return true;
        case pushChanges_key+settings_key_offset:
          setPVar(pushChanges, aPropValue->boolValue());
          return true;
        // State properties
        case localPriority_key+states_key_offset:
          setPVar(localPriority, aPropValue->boolValue());
          return true;
      }
    }
  }
  // not my field, let base class handle it
  return inherited::accessField(aMode, aPropValue, aPropertyDescriptor);
}