Madness!
Okay, well, here'd be my first guess:
Create a Modbus data source, Create a data point for register 71 reading a 2 byte unsigned integer, you configuration may look like this:
{
"dataSources":[
{
"xid":"DS_cba83af5-38f8-4830-a07a-daeda155bc5b",
"name":"wonky-byte-packing",
"enabled":false,
"type":"MODBUS_IP",
"alarmLevels":{
"POINT_WRITE_EXCEPTION":"URGENT",
"POLL_ABORTED":"URGENT",
"DATA_SOURCE_EXCEPTION":"URGENT",
"POINT_READ_EXCEPTION":"URGENT"
},
"purgeType":"YEARS",
"updatePeriodType":"SECONDS",
"transportType":"TCP",
"encapsulated":false,
"host":"localhost",
"port":502,
"contiguousBatches":false,
"createSlaveMonitorPoints":false,
"discardDataDelay":0,
"ioLogFileSizeMBytes":1.0,
"logIO":false,
"maxHistoricalIOLogs":1,
"maxReadBitCount":2000,
"maxReadRegisterCount":125,
"maxWriteRegisterCount":120,
"multipleWritesOnly":false,
"quantize":false,
"retries":2,
"timeout":500,
"updatePeriods":20,
"editPermission":"",
"purgeOverride":false,
"purgePeriod":1
}
],
"dataPoints":[
{
"xid":"DP_00c2c077-a245-4768-aed8-774bb0f7f982",
"name":"OccupiedTemps",
"enabled":false,
"loggingType":"ALL",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"AVERAGE",
"purgeType":"YEARS",
"pointLocator":{
"range":"HOLDING_REGISTER",
"modbusDataType":"TWO_BYTE_INT_UNSIGNED",
"writeType":"SETTABLE",
"additive":0.0,
"bit":0,
"charset":"ASCII",
"multiplier":1.0,
"offset":71,
"registerCount":0,
"slaveId":1,
"slaveMonitor":false
},
"eventDetectors":[
],
"plotType":"SPLINE",
"rollup":"NONE",
"unit":"",
"chartColour":"",
"chartRenderer":{
"type":"IMAGE",
"timePeriodType":"DAYS",
"numberOfPeriods":1
},
"dataSourceXid":"DS_cba83af5-38f8-4830-a07a-daeda155bc5b",
"defaultCacheSize":1,
"deviceName":"wonky-byte-packing",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":1,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"textRenderer":{
"type":"ANALOG",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"format":"0.00"
},
"tolerance":0.0
}
]
}
Next we'll need a scripting data source and four points, "Set Occupied Heating Setpoint", "Set Occupied Cooling Setpoint", "Occupied Heating Setpoint" and "Occupied Cooling Setpoint". I'll just use their acronyms as variable names. They're all settable numeric. The script body may look like (the modbus point from step 1 will be in the context, let's say variable name "occupiedSetpoints",
if( sohs.value != -1 ) { //Someone is trying to set the heating setpoint
if( socs.value != -1) {
occupiedSetpoints.set( socs.value<<8 | sohs.value ); //we may need to set both
sohs.set( -1 ); //Clear it so we don't keep setting too much
socs.set( -1 );
} else {
occupiedSetpoints.set( ocs.value<<8 | sohs.value );
sohs.set( -1 );
}
} else if( socs.value != -1 ) {
occupiedSetpoints.set( socs.value<<8 | ohs.value );
socs.set( -1 );
}
That whole data source's configuration might look like this:
{
"dataSources":[
{
"xid":"DS_760b73e5-5444-4a8e-9258-48648b093b04",
"name":"HandleWonkiness",
"enabled":false,
"type":"SCRIPTING",
"alarmLevels":{
"SCRIPT_ERROR":"URGENT",
"DATA_TYPE_ERROR":"URGENT",
"POLL_ABORTED":"URGENT",
"LOG_ERROR":"URGENT"
},
"purgeType":"YEARS",
"context":[
{
"varName":"occupiedSetpoints",
"dataPointXid":"DP_00c2c077-a245-4768-aed8-774bb0f7f982"
}
],
"logLevel":"NONE",
"cronPattern":"0\/1 * * * * ?",
"executionDelaySeconds":0,
"historicalSetting":false,
"script":"if( sohs.value != -1 ) { \/\/Someone is trying to set the heating setpoint\r\n if( socs.value != -1) {\r\n occupiedSetpoints.set( socs.value<<8 | sohs.value ); \/\/we may need to set both\r\n sohs.set( -1 ); \/\/Clear it so we don't keep setting too much\r\n socs.set( -1 );\r\n } else {\r\n occupiedSetpoints.set( ocs.value<<8 | sohs.value );\r\n sohs.set( -1 );\r\n }\r\n} else if( socs.value != -1 ) {\r\n occupiedSetpoints.set( socs.value<<8 | ohs.value );\r\n socs.set( -1 );\r\n}",
"scriptPermissions":{
"customPermissions":"",
"dataPointReadPermissions":"superadmin",
"dataPointSetPermissions":"superadmin",
"dataSourcePermissions":"superadmin"
},
"editPermission":"",
"purgeOverride":false,
"purgePeriod":1
}
],
"dataPoints":[
{
"xid":"DP_1dd39f06-c252-4bf2-bd75-27108ad1812c",
"name":"Set Occupied Heating Setpoint",
"enabled":true,
"loggingType":"ALL",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"AVERAGE",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"NUMERIC",
"settable":true,
"varName":"sohs"
},
"eventDetectors":[
],
"plotType":"SPLINE",
"rollup":"NONE",
"unit":"",
"templateXid":"Numeric_Default",
"chartColour":"",
"chartRenderer":{
"type":"IMAGE",
"timePeriodType":"DAYS",
"numberOfPeriods":1
},
"dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04",
"defaultCacheSize":1,
"deviceName":"HandleWonkiness",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":1,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"textRenderer":{
"type":"ANALOG",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"format":"0.00"
},
"tolerance":0.0
},
{
"xid":"DP_b57761e9-ce04-41d2-9ea8-8018eab7ec4e",
"name":"Set Occupied Cooling Setpoint",
"enabled":true,
"loggingType":"ALL",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"AVERAGE",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"NUMERIC",
"settable":true,
"varName":"socs"
},
"eventDetectors":[
],
"plotType":"SPLINE",
"rollup":"NONE",
"unit":"",
"templateXid":"Numeric_Default",
"chartColour":"",
"chartRenderer":{
"type":"IMAGE",
"timePeriodType":"DAYS",
"numberOfPeriods":1
},
"dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04",
"defaultCacheSize":1,
"deviceName":"HandleWonkiness",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":1,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"textRenderer":{
"type":"ANALOG",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"format":"0.00"
},
"tolerance":0.0
},
{
"xid":"DP_654eebab-d687-4083-8f09-80a160d9e375",
"name":"Occupied Heating Setpoint",
"enabled":true,
"loggingType":"ALL",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"AVERAGE",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"NUMERIC",
"settable":true,
"varName":"ohs"
},
"eventDetectors":[
],
"plotType":"SPLINE",
"rollup":"NONE",
"unit":"",
"templateXid":"Numeric_Default",
"chartColour":"",
"chartRenderer":{
"type":"IMAGE",
"timePeriodType":"DAYS",
"numberOfPeriods":1
},
"dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04",
"defaultCacheSize":1,
"deviceName":"HandleWonkiness",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":1,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"textRenderer":{
"type":"ANALOG",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"format":"0.00"
},
"tolerance":0.0
},
{
"xid":"DP_879c5c66-784c-4d5e-960e-96cda59bb260",
"name":"Occupied Cooling Setpoint",
"enabled":true,
"loggingType":"ALL",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"AVERAGE",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"NUMERIC",
"settable":true,
"varName":"ocs"
},
"eventDetectors":[
],
"plotType":"SPLINE",
"rollup":"NONE",
"unit":"",
"templateXid":"Numeric_Default",
"chartColour":"",
"chartRenderer":{
"type":"IMAGE",
"timePeriodType":"DAYS",
"numberOfPeriods":1
},
"dataSourceXid":"DS_760b73e5-5444-4a8e-9258-48648b093b04",
"defaultCacheSize":1,
"deviceName":"HandleWonkiness",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":1,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"textRenderer":{
"type":"ANALOG",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"format":"0.00"
},
"tolerance":0.0
}
]
}
Note that you could use a different value than -1, but most people don't set their A/C values anywhere near freezing so you're probably okay there.
And finally we'll need to have some point links from the modbus point to our two points for each of the current setpoints. That means in the point link source point for both links and "Occpied Heating Setpoint" is the target of one (The other is Occupied Cooling Setpoint).
One link will have the body,
return source.value & 0xFF;
the other will be
return source.value>>8 & 0xFF;
Those may have a configuration like:
{
"pointLinks":[
{
"xid":"PL_eacbf9a9-d919-49f4-88d1-ed59cb19c243",
"sourcePointId":"DP_00c2c077-a245-4768-aed8-774bb0f7f982",
"targetPointId":"DP_879c5c66-784c-4d5e-960e-96cda59bb260",
"event":"CHANGE",
"logLevel":"NONE",
"disabled":false,
"script":"return source.value>>8 & 0xFF;",
"scriptPermissions":{
"customPermissions":"",
"dataPointReadPermissions":"superadmin",
"dataPointSetPermissions":"superadmin",
"dataSourcePermissions":"superadmin"
},
"writeAnnotation":false
},
{
"xid":"PL_94a9ac52-4770-4b90-b3a3-a294d60b2d03",
"sourcePointId":"DP_00c2c077-a245-4768-aed8-774bb0f7f982",
"targetPointId":"DP_654eebab-d687-4083-8f09-80a160d9e375",
"event":"CHANGE",
"logLevel":"NONE",
"disabled":false,
"script":"return source.value & 0xFF;",
"scriptPermissions":{
"customPermissions":"",
"dataPointReadPermissions":"superadmin",
"dataPointSetPermissions":"superadmin",
"dataSourcePermissions":"superadmin"
},
"writeAnnotation":false
}
]
}