Raspberry Pi Fan

Here you go. note that this is actually authored by Andreas Spiess (Variable Speed Cooling Fan for Raspberry Pi using PWM (video#138) – SensorsIOT)
i have only added the code to publish fan speed and temperature to my MQTT broker, from where HA is picking it up. i have also trimmed the code down a bit to exclude the stuff he had for battery monitoring etc.

fancontrol.py

#!/usr/bin/env python3
# Author: Andreas Spiess
import os
import time
from time import sleep
import signal
import sys
import RPi.GPIO as GPIO
import paho.mqtt.publish as publish

fanPin = 17 # The pin ID, edit here to change it
batterySensPin = 18

desiredTemp = 40 # The maximum temperature in Celsius after which we trigger the fan

fanSpeed=100
sum=0
pTemp=15
iTemp=0.4

def Shutdown():  
    fanOFF()
    os.system("sudo shutdown -h 1")
    sleep(100)
def getCPUtemperature():
    res = os.popen('vcgencmd measure_temp').readline()
    temp =(res.replace("temp=","").replace("'C\n",""))
    #print("temp is {0}".format(temp)) #Uncomment here for testing
    return temp
def fanOFF():
    myPWM.ChangeDutyCycle(0)   # switch fan off
    return()
def handleFan():
    global fanSpeed,sum
    actualTemp = float(getCPUtemperature())
    diff=actualTemp-desiredTemp
    sum=sum+diff
    pDiff=diff*pTemp
    iDiff=sum*iTemp
    fanSpeed=pDiff +iDiff
    if fanSpeed>100:
        fanSpeed=100
    if fanSpeed<15:
        fanSpeed=0
    if sum>100:
        sum=100
    if sum<-100:
        sum=-100
    #print("actualTemp %4.2f TempDiff %4.2f pDiff %4.2f iDiff %4.2f fanSpeed %5d" % (actualTemp,diff,pDiff,iDiff,fanSpeed))
    myPWM.ChangeDutyCycle(fanSpeed)
    msgs = [{'topic':"RPI/Temperature", 'payload':actualTemp},
                ("RPI/FanSpeed", fanSpeed, 0, False)]
    publish.multiple(msgs, hostname="[mqtt server]", 
          auth = {'username':"[mqtt server username]", 'password':"[mqtt server password]"})
    return()
def on_disconnect(client, userdata, rc):
    logging.info("disconnecting reason  "  +str(rc))
    client.connected_flag=False
    client.disconnect_flag=True
def handleBattery():
    #print (GPIO.input(batterySensPin))
    if GPIO.input(batterySensPin)==0:
        ("Shutdown()")
        sleep(5)
        Shutdown()
    return()
def setPin(mode): # A little redundant function but useful if you want to add logging
    GPIO.output(fanPin, mode)
    return()
try:
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(fanPin, GPIO.OUT)
    myPWM=GPIO.PWM(fanPin,50)
    myPWM.start(50)
    #GPIO.setup(batterySensPin, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
    GPIO.setwarnings(False)
    fanOFF()
    while True:
        handleFan()
        #handleBattery()
        sleep(5) # Read the temperature every 5 sec, increase or decrease this limit if you want 
except KeyboardInterrupt: # trap a CTRL+C keyboard interrupt 
    fanOFF()
    GPIO.cleanup() # resets all GPIO ports used by this program
except:
    print("probably mqtt connection failed")