@Override
 protected TextRenderer _createTextRenderer(String pointId, int caseNum) {
   if (getNumCases() > 1 || (enocianType != null && enocianType.getCases() != null)) {
     for (DataField dataField : enocianType.getCases().get(caseNum).getDatafields()) {
       if (!dataField.isLearn() && pointId.equals(dataField.getShortcut())) {
         if (dataField.getRange() != null) {
           return new AnalogRenderer("0.00", dataField.getUnit());
         } else {
           List<Item> items = dataField.getItems();
           if (items.get(0).getScale() != null || items.size() != 2) {
             MultistateRenderer result = new MultistateRenderer();
             for (Item item : items) {
               result.addMultistateValue(item.getValue(), item.getDescription(), null);
             }
             return result;
           } else {
             if (items.get(0).getValue() == 0) {
               return new BinaryTextRenderer(
                   items.get(0).getDescription(), null, items.get(1).getDescription(), name);
             } else {
               return new BinaryTextRenderer(
                   items.get(1).getDescription(), null, items.get(0).getDescription(), name);
             }
           }
         }
       }
     }
   } else {
     for (DataField dataField : enocianType.getDatafields()) {
       if (!dataField.isLearn() && pointId.equals(dataField.getShortcut())) {
         if (dataField.getRange() != null) {
           return new AnalogRenderer("0.00", dataField.getUnit());
         } else {
           List<Item> items = dataField.getItems();
           if (items.get(0).getScale() != null || items.size() != 2) {
             MultistateRenderer result = new MultistateRenderer();
             for (Item item : items) {
               result.addMultistateValue(item.getValue(), item.getDescription(), null);
             }
             return result;
           } else {
             if (items.get(0).getValue() == 0) {
               return new BinaryTextRenderer(
                   items.get(0).getDescription(), null, items.get(1).getDescription(), name);
             } else {
               return new BinaryTextRenderer(
                   items.get(1).getDescription(), null, items.get(0).getDescription(), name);
             }
           }
         }
       }
     }
   }
   return null;
 }
  @Override
  protected void _parseTelegram(RadioPacket radio, TelegramData t) {
    List<DataField> postponedProcessing = new LinkedList<>();
    Map<String, Scale> refs = new HashMap<>();

    byte[] userData = radio.getUserData();
    int c = getCaseFromTelegram(radio);
    if (getNumCases() > 1 || (enocianType != null && enocianType.getCases() != null)) {
      for (DataField dataField : enocianType.getCases().get(c).getDatafields()) {
        int value =
            ArrayUtils.bitRangeValue(userData, dataField.getBitoffs(), dataField.getBitsize());

        if (dataField.isLearn()) {
          if (value == 0) {
            t.setLearn(true);
          }
        } else {
          Scale scale = dataField.getScale();
          if (scale != null) {
            if (scale.getRef() != null) {
              postponedProcessing.add(dataField);
              continue;
            }
            t.addValue(
                dataField.getShortcut(),
                new NumericValue(getScaledValue(value, dataField.getRange(), scale)));
          } else if (dataField.getItems() != null) {
            List<Item> items = dataField.getItems();
            if (items.get(0).getScale() != null || items.size() != 2) { // multistate
              t.addValue(dataField.getShortcut(), new MultistateValue(value));
              if (items.get(0).getScale() != null) {
                for (Item item : items) {
                  if (item.getValue() == value) {
                    refs.put(dataField.getShortcut(), item.getScale());
                    break;
                  }
                }
              }
            } else {
              t.addValue(dataField.getShortcut(), new BinaryValue(value == 1));
            }
          }
        }
      }
    } else {
      for (DataField dataField : enocianType.getDatafields()) {
        int value =
            ArrayUtils.bitRangeValue(userData, dataField.getBitoffs(), dataField.getBitsize());

        if (dataField.isLearn()) {
          if (value == 0) {
            t.setLearn(true);
          }
        } else {
          Scale scale = dataField.getScale();
          if (scale != null) {
            if (scale.getRef() != null) {
              postponedProcessing.add(dataField);
              continue;
            }
            t.addValue(
                dataField.getShortcut(),
                new NumericValue(getScaledValue(value, dataField.getRange(), scale)));
          } else if (dataField.getItems() != null) {
            List<Item> items = dataField.getItems();
            if (items.get(0).getScale() != null || items.size() != 2) { // multistate
              t.addValue(dataField.getShortcut(), new MultistateValue(value));
              if (items.get(0).getScale() != null) {
                for (Item item : items) {
                  if (item.getValue() == value) {
                    refs.put(dataField.getShortcut(), item.getScale());
                    break;
                  }
                }
              }
            } else {
              t.addValue(dataField.getShortcut(), new BinaryValue(value == 1));
            }
          }
        }
      }
    }

    if (!postponedProcessing.isEmpty()) { // required to dynamically resolve reference variables
      for (DataField dataField : postponedProcessing) {
        int value =
            ArrayUtils.bitRangeValue(userData, dataField.getBitoffs(), dataField.getBitsize());
        String ref = dataField.getScale().getRef();
        Scale scale = refs.get(ref);
        if (scale != null) {
          t.addValue(
              dataField.getShortcut(),
              new NumericValue(getScaledValue(value, dataField.getRange(), scale)));
        }
      }
    }
  }