Skip to content

Instantly share code, notes, and snippets.

@ndfred
Last active July 23, 2023 07:21
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save ndfred/b373eeafc4f5b0870c1b8857041289a9 to your computer and use it in GitHub Desktop.
Fetch data from a Hildebrand Glow device over MQTT
import datetime
import logging
import json
import paho.mqtt.client as mqtt # pip3 install paho-mqtt
GLOW_LOGIN = "GLOW_LOGIN"
GLOW_PASSWORD = "GLOW_PASSWORD"
GLOW_DEVICE_ID = "GLOW_DEVICE_ID"
# Fields gathered from the ZigBee Smart Energy Standard document
# 0702: Metering
# - 00: Reading Information Set
# - 00: CurrentSummationDelivered: meter reading
# - 01: CurrentSummationReceived
# - 02: CurrentMaxDemandDelivered
# - 07: ReadingSnapshotTime (UTC time)
# - 14: Supply Status (enum): 0x2 is on
# - 02: Meter Status
# - 00: Status (bit map): 10 means power quality event
# - 03: Formatting
# - 00: UnitofMeasure (enum): 00 means kWh, 01 means m3
# - 01: Multiplier
# - 02: Divisor
# - 03: SummationFormatting (bit map):
# 2B means 3 digits after the decimal point, 2 digits before the decimal point
# FB means 3 digits after the decimal point, 16 digits before the decimal point,
# no leading zeros
# - 04: DemandFormatting
# - 06: MeteringDeviceType: 00 means Electric Metering, 80 means Mirrored Gas Metering
# - 07: SiteID: MPAN encoded in UTF-8
# - 08: MeterSerialNumber (string)
# - 12: AlternativeUnitofMeasure (enum)
# - 04: Historical Consumption
# - 00: InstantaneousDemand (signed): current consumption
# - 01: CurrentDayConsumptionDelivered
# - 30: CurrentWeekConsumptionDelivered
# - 40: CurrentMonthConsumptionDelivered
# - 0C: Alternative Historical Consumption
# - 01: CurrentDayConsumptionDelivered
# - 30: CurrentWeekConsumptionDelivered
# - 40: CurrentMonthConsumptionDelivered
# 0705: Prepayment
# - 00: Prepayment Information Set
# - 00: PaymentControlConfiguration (bit map)
# - 01: CreditRemaining (signed)
# 0708: Device Management
# - 01: Supplier Control Attribute Set
# - 01: ProviderName (string)
def on_connect(client, _userdata, _flags, result_code):
if result_code != mqtt.MQTT_ERR_SUCCESS:
logging.error("Error connecting: %d", result_code)
return
result_code, _message_id = client.subscribe("SMART/HILD/" + GLOW_DEVICE_ID)
if result_code != mqtt.MQTT_ERR_SUCCESS:
logging.error("Couldn't subscribe: %d", result_code)
return
logging.info("Connected and subscribed")
def on_message(_client, _userdata, message):
payload = json.loads(message.payload)
current_time = datetime.datetime.now().strftime("%H:%M:%S")
electricity_consumption = int(payload["elecMtr"]["0702"]["04"]["00"], 16)
electricity_daily_consumption = int(payload["elecMtr"]["0702"]["04"]["01"], 16)
electricity_weekly_consumption = int(payload["elecMtr"]["0702"]["04"]["30"], 16)
electricity_monthly_consumption = int(payload["elecMtr"]["0702"]["04"]["40"], 16)
electricity_multiplier = int(payload["elecMtr"]["0702"]["03"]["01"], 16)
electricity_divisor = int(payload["elecMtr"]["0702"]["03"]["02"], 16)
electricity_meter = int(payload["elecMtr"]["0702"]["00"]["00"], 16)
gas_daily_consumption = int(payload["gasMtr"]["0702"]["0C"]["01"], 16)
gas_weekly_consumption = int(payload["gasMtr"]["0702"]["0C"]["30"], 16)
gas_monthly_consumption = int(payload["gasMtr"]["0702"]["0C"]["40"], 16)
gas_multiplier = int(payload["gasMtr"]["0702"]["03"]["01"], 16)
gas_divisor = int(payload["gasMtr"]["0702"]["03"]["02"], 16)
gas_meter = int(payload["gasMtr"]["0702"]["00"]["00"], 16)
electricity_daily_consumption = electricity_daily_consumption * electricity_multiplier / electricity_divisor
electricity_weekly_consumption = electricity_weekly_consumption * electricity_multiplier / electricity_divisor
electricity_monthly_consumption = electricity_monthly_consumption * electricity_multiplier / electricity_divisor
electricity_meter = electricity_meter * electricity_multiplier / electricity_divisor
gas_daily_consumption = gas_daily_consumption * gas_multiplier / gas_divisor
gas_weekly_consumption = gas_weekly_consumption * gas_multiplier / gas_divisor
gas_monthly_consumption = gas_monthly_consumption * gas_multiplier / gas_divisor
gas_meter = gas_meter * gas_multiplier / gas_divisor
assert(int(payload["elecMtr"]["0702"]["03"]["00"], 16) == 0) # kWh
assert(int(payload["gasMtr"]["0702"]["03"]["01"], 16) == 1) # m3
assert(int(payload["gasMtr"]["0702"]["03"]["12"], 16) == 0) # kWh
logging.info("Reading at %s", current_time)
logging.info("* electricity consumption: %dW", electricity_consumption)
logging.info("* daily electricity consumption: %.3fkWh", electricity_daily_consumption)
logging.info("* weekly electricity consumption: %.3fkWh", electricity_weekly_consumption)
logging.info("* monthly electricity consumption: %.3fkWh", electricity_monthly_consumption)
logging.info("* electricity meter: %.3fkWh", electricity_meter)
logging.info("* daily gas consumption: %.3fkWh", gas_daily_consumption)
logging.info("* weekly gas consumption: %.3fkWh", gas_weekly_consumption)
logging.info("* monthly gas consumption: %.3fkWh", gas_monthly_consumption)
logging.info("* gas meter: %.3fm3", gas_meter)
logging.info("Full payload: %s", json.dumps(payload, indent=2))
def loop():
logging.basicConfig(level=logging.DEBUG, format='%(message)s')
client = mqtt.Client()
client.username_pw_set(GLOW_LOGIN, GLOW_PASSWORD)
client.on_connect = on_connect
client.on_message = on_message
client.connect("glowmqtt.energyhive.com")
client.loop_forever()
if __name__ == "__main__":
loop()
@peter-dolkens
Copy link

From my reading, lines 90/91 should be:

    assert(int(payload["gasMtr"]["0702"]["03"]["00"], 16) == 0) # m3
    assert(int(payload["gasMtr"]["0702"]["03"]["00"], 16) == 1) # kWh

payload["gasMtr"]["0702"]["03"]["00"] is the multiplier, so unrelated to the unit.

payload["gasMtr"]["0702"]["03"]["12"] is the enum describing the alternate unit of measure.

You appear to be reading from 2 different units - with your aggregates being read in from the alternate meter reading in kWh, and your instantaneous usage being read in from the primary meter.

I would imagine it would be more reliable to simply read from the primary meter, and adjust the units to match the indicated unit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment