Jython Rule Results in Strange Character When Writing "°C" to a String

I’ve written a jython rule to summarise heating data for a room in a string to use in the frame title in a sitemap. The rule concatenates the following three items into a string:

  1. room temperature
  2. room setpoint
  3. room radiator valve aperture in percent

For example, if the temperature in a room is 20.0 degrees, the setpoint is 20.5 degrees and the radiator valve aperture is 88% I’d like to see that summarised as “20.2°C - (20°C/88%)”.

The string is built with the following line of code:

HeatingBlueRoomInfoSummary_String = "{}°C - ({}°C/{}%)".format(str(roomTemperatureItem.state), str(roomSetpointItem.state), str(valveOpeningInteger))

It’s nearly right but in the sitemap both on the Android app and in a browser the string shows as:

20.0°C - (20.5°C/88%)

I can’t seem to get rid of those “” characters. Does anyone know the secret sauce to build a string with “°C” but without “” characters?

Also, while I’m here, is it possible to format the number of decimal places from an item? The binding that I use to communicate with the heating hardware passes oodles of decimal places into the items. For example, the valve aperture shown above as 88% was acutally something crazy like 88.482672346%. At the moment in the rule I cast the item to an integer like this:

valveOpeningInteger = int(float(str(roomValveOpeningItem.state)))

No big deal at all but I’d like to know if it’s possible to format items to a certain number of decimal places in the format keyword.

Use unicode (note the ‘u’ below)… also, format converts things into strings, so you don’t have to…

HeatingBlueRoomInfoSummary_String = u"{}°C - ({}°C/{}%)".format(roomTemperatureItem.state, roomSetpointItem.state, valveOpeningInteger)

You may want to learn about UoM and QuanityType though.

If the decimals are not wanted in the UI, then format them in the label of the Item or sitemap element. If it is for logging, you can use this…

LOG.warn("test: {:.1f}".format(55555.55555))

This is a bit cleaner than your example. I recommend using the Java/OH methods, when available.

valveOpeningInteger = roomValveOpeningItem.state.intValue()

More info here…

… and here…

Thanks for replying, Scott. I’ve prefixed the string with a u like this:

HeatingBlueRoomInfoSummary_String = u"{}°C - ({}°C/{}%)".format(str(roomTemperatureItem.state), str(roomSetpointItem.state), str(valveOpeningInteger))

And this resulted in the following error in the log:

2020-10-27 16:00:39.239 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'af239d5e-8ce4-4f22-99ed-cf9de9db1a1b': Fail to execute action: 1
2020-10-27 16:00:40.971 [ERROR] [n.jsr223.jython.Room heating summary] - Traceback (most recent call last):
  File "/etc/openhab2/automation/lib/python/core/log.py", line 51, in wrapper
    return fn(*args, **kwargs)
  File "<script>", line 30, in room_heating_summary
UnicodeEncodeError: 'ascii' codec can't encode character u'\xb0' in position 4: ordinal not in range(128)

I did some googling and found references to the “.encode(‘utf-8’)” which I tried applying liberally throughout that line of code and although it did resolve the runtime error it didn’t remove the “” character. Any ideas?

I’ve included the entire rule below in case there’s any context around the string building that helps.

@rule("Room heating summary", description="This rule builds a string that summarises the main heating data for a room - Temp (Set point / Valve opening), e.g. - 20.5°C (20.0°C/45%)")
@when("Member of gHeatingRoomData changed")
def room_heating_summary(event):

    log = logging.getLogger("org.eclipse.smarthome.model.script.heating")
    log.info("Heating: setpoint preset from schedule rule started...")

    triggeringRoomGroup     = [group for group in ir.getItem("gRoom").members if group.type == "Group" and ir.getItem(event.itemName) in group.members][0]
    roomSetpointItem        = [group for group in ir.getItem(str(triggeringRoomGroup.name)).members if group in ir.getItem("gRoomSetPoint").members][0]
    roomTemperatureItem     = [group for group in ir.getItem(str(triggeringRoomGroup.name)).members if group in ir.getItem("gRoomThermostat").members][0]
    roomValveOpeningItem    = [group for group in ir.getItem(str(triggeringRoomGroup.name)).members if group in ir.getItem("gTotalValveOpening").members][0]

    valveOpeningInteger = int(float(str(roomValveOpeningItem.state)))

    log.info("Heating: In room ({}) setpoint item name is ({}),temperature item name is ({}) with value ({}), valve opening item is ({}) with value ({}%).".format(
        str(triggeringRoomGroup.name), str(roomSetpointItem.name), str(roomSetpointItem.name), str(roomTemperatureItem.state), str(roomValveOpeningItem.name), str(roomValveOpeningItem.state)))

    HeatingBlueRoomInfoSummary_Item = "Heating{}InfoSummary".format(str(triggeringRoomGroup.name))
    HeatingBlueRoomInfoSummary_String = u"{}°C - ({}°C/{}%)".format(str(roomTemperatureItem.state).encode('utf-8'), str(roomSetpointItem.state).encode('utf-8'), str(valveOpeningInteger)).encode('utf-8')
    events.sendCommand(ir.getItem(str(HeatingBlueRoomInfoSummary_Item)), str(HeatingBlueRoomInfoSummary_String))

Incidentally, for anyone who’s interested, the degrees symbol I’m using can be entered by holding the Alt key then typing 248 on the numeric keypad and then releasing the Alt key.

What is on line 30? My guess is that it is where you are using the rule decorator. You are using special characters in the description, so use a unicode string there too. However, I’m not sure that will work, but I have a big enhancement almost ready to merge that adds a lot of handling for special characters, lik ein the rule description. So, you may just need to take the description out for now.

Adding another logger is unnecessary… just use the one that is added as an attribute to the rule’s callback function, like…

room_heating_summary.info("Heating: setpoint preset from schedule rule started...")

That is for Windows. For Linux, use ctrl-shift-u then b0 then enter. Another handy one is ctrl-shift-u b2, for the superscript ².

That runtime error referring to line 30 is triggered by the following code:

HeatingBlueRoomInfoSummary_String = u"{}°C - ({}°C/{}%)".format(roomTemperatureItem.state, roomSetpointItem.state, valveOpeningInteger)

If I add “.encode(‘utf-8’)” at the end of each of the three variables in that code then the runtime error doesn’t occur but I still get the “” when the string is displayed.

I use that logger because I have various rules related to heating, some still written in DSL, and I have a separate log file configured in org.ops4j.pax.logging.cfg that I want all heating related messages to be written to rather than them all flooding openhab.log.